firebase UI 在生成带有 minifyEnabled 设置为 true 的签名 APK 后输出空白

firebase UI outputs blank after generate signed APK with minifyEnabled set true

我的应用程序制作快完成了,但我一直在尝试解决最后一个问题。我制作了一个 activity 来显示使用 FirestoreRecyclerAdapter 从 Firestore 获取的数据,并且在我的模拟器上使用 运行 时它可以完美运行。但是在我使用 minifyEnabled:true 生成签名的 APK 并将其安装到我的设备上之后,RecyclerView 只显示空白。当我设置 minifyEnabled:false 时,它工作得很好。以下是我的代码

对于 TransactionRecycler class

public class TransactionRecycler {
    private String payment_date;
    private String status;
    private String meternumber;
    public String reference;
    private String amount_formatted;
    private Double PurchasedUnits;
    private Double FreeUnits;
    private Double Vat;
    public String id;
    private String payment_time;
  //  public String state;
    private String token;
    public String address;
    private String account_name;



    public TransactionRecycler(String payment_date, String status, String meternumber, String reference, String id, String amount_formatted, String payment_time, String token, Double PurchasedUnits,
                               Double FreeUnits, Double Vat, String account_name, String address){
        this.payment_date = payment_date;
        this.status = status;
        this.meternumber = meternumber;
        this.reference = reference;
        this.amount_formatted = amount_formatted;
        this.PurchasedUnits = PurchasedUnits;
        this.Vat = Vat;
     //   this.state = state;
        this.FreeUnits = FreeUnits;
        this.id = id;
        this.payment_time = payment_time;
        this.token = token;
        this.account_name = account_name;
        this.address = address;
    }

    public TransactionRecycler(){};

    public void setDate(String payment_date){this.payment_date = payment_date;}
    public void setId(String id){this.id =id;}
   // public void setState(String id){this.state =state;}


    public String getPayment_date(){return payment_date;}
    public String getStatus(){return status;}
    public String getMeternumber(){return meternumber;}
    public String getReference(){return reference;}
    public String getAddress(){return address;}

    public String getAmount_formatted(){return amount_formatted;}
    public Double getPurchasedUnits(){return PurchasedUnits;}
    public Double getFreeUnits(){return FreeUnits;}
    public Double getVat(){return Vat;}
    public String getId(){return id;}
   // public String getstate(){return state;}
   public String getPayment_time(){return payment_time;}
    public String getToken(){return token;}
    public String getAccount_name(){return account_name;}
}

对于 TRansactionActivity

public class TransactionActivity extends AppCompatActivity {

    private static final String TAG = "TransactionActivity";
    private final static String strUrlId= "https://www.eliminateramp.com?exmen=";
    private static final int SERVICE_CHARGE = 100;
    private static final String PLEASE_CHECK_YOUR_METER_NUMBER_AND_TRY_AGAIN = "Please check your meter number and try again";
    private static final String COULDN_T_CONNECT_AT_THIS_TIME_PLEASE_TRY_AGAIN = "Couldn't connect at this time, Please try again";
    private static final String FAILLED = "failled";
    private static final String NOT_FOUND = "NOT FOUND";
    private FirebaseAuth mAuth;
    private String userID;
    private RecyclerView mTransactionList;
    protected LinearLayoutManager linearLayoutManager;
    public FirestoreRecyclerAdapter adapter;
    private SlidingUpPanelLayout mLayout;

    private NavigationView navigation;
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mToggle;
    private static final String SPACE = " ";
    private static String phone;
    private Button charname;
    private TextView emailview;
    private TextView fullnameview;
    private TextView priceidrc;
    private static TextView post_meterno;
    private static String aamount;


    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.top_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transaction);
        mTransactionList = findViewById(R.id.friend_listinc);
        mLayout = findViewById(R.id.sliding_layout);
        mAuth = FirebaseAuth.getInstance();
        userID = mAuth.getCurrentUser().getUid();
        Log.d("Error","The UID is: "+userID);

        Toolbar mToolbar = findViewById(R.id.nav_actionbar);
        setSupportActionBar(mToolbar);
        mDrawerLayout = findViewById(R.id.drawerlayout);
        navigation = findViewById(R.id.navigationview);

        mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.open, R.string.close);

        View headerView = navigation.getHeaderView(0);

        emailview = headerView.findViewById(R.id.emailviewraw);
        fullnameview = headerView.findViewById(R.id.fullnameviewid);
        charname = headerView.findViewById(R.id.charnameid);

        mDrawerLayout.addDrawerListener(mToggle);
        mToggle.syncState();
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        try{
            getUserData();
        }catch (Exception e){
            Log.d(TAG, "Failed to get user Data at this time coz: "+e);

        }

        ImageView imgview = findViewById(R.id.closeid);
        imgview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mLayout.animate();
                mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
            }
        });

        charname.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(TransactionActivity.this,UpdateActivity.class));
            }
        });

        init();
        inTrans();
        initInstances();


    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        return mToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    }

    private void initInstances() {
        //  getSupportActionBar().setHomeButtonEnabled(true);
        //   getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        navigation = findViewById(R.id.navigationview);
        navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                int id = menuItem.getItemId();
                switch (id) {
                    case R.id.nav_home:
                        //Do some thing here
                        // add navigation drawer item onclick method here
                        Intent intent2 = new Intent(TransactionActivity.this, HomePageActivity.class);
                        startActivity(intent2);
                        break;
                    case R.id.nav_estimate:
                        mDrawerLayout.closeDrawer(GravityCompat.START);
                        break;
                    //Do some thing here
                    // add navigation drawer item onclick method here
                    case R.id.nav_Settings:
                        //Do some thing here
                        // add navigation drawer item onclick method here
                        Intent intent3 = new Intent(TransactionActivity.this, HelpActivity.class);
                        startActivity(intent3);
                        break;

                    case R.id.nav_hc:
                        //Do some thing here
                        // add navigation drawer item onclick method here
                        Intent intent = new Intent(TransactionActivity.this, HelpCenterActivity.class);
                        startActivity(intent);
                        break;
                }
                return false;
            }
        });

    }

    private void init(){
        linearLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false);
        mTransactionList.setLayoutManager(linearLayoutManager);
    }

    private void inTrans(){

        Query query = FirebaseFirestore.getInstance().collection("user-orders").document(userID).collection("successful-orders").orderBy("RequestedOn");

        FirestoreRecyclerOptions<TransactionRecycler> response = new FirestoreRecyclerOptions.Builder<TransactionRecycler>()
                .setQuery(query, TransactionRecycler.class)
                .build();

        adapter = new FirestoreRecyclerAdapter<TransactionRecycler, TransactionRecyclerHolder>(response){

            @Override
            public TransactionRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {

                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.transaction_list, parent, false);
                return new TransactionRecyclerHolder(view);
            }
            @Override
            public void onError(@NonNull FirebaseFirestoreException e) {
                Log.e("error", e.getMessage());
            }

            @Override
            protected void onBindViewHolder(@NonNull TransactionRecyclerHolder holder, int position, @NonNull final TransactionRecycler model) {

                //progressBar.setVisibility(View.GONE);
                holder.setDate(model.getPayment_date());
                holder.setStatus(model.getStatus());
                holder.setReference(model.getId());
                holder.setMeterno(model.getMeternumber());
                holder.setPrice(model.getAmount_formatted());


                mLayout.setFadeOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mLayout.animate();
                        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
                    }
                });
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mLayout.setPanelHeight(4);

                        mLayout.animate();
                       // mLayout.setAnchorPoint(2.0f);
                        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);

                        priceidrc = findViewById(R.id.priceidsc);
                        TextView dateidrc = findViewById(R.id.dateidsc);
                        TextView referenceidrc = findViewById(R.id.refidsc);
                        TextView statusidrc = findViewById(R.id.statusidsc);
                        TextView meternoidrc = findViewById(R.id.meternoidsc);
                        TextView paymenttimeidrc = findViewById(R.id.paymenttimeidsc);
                        TextView tokenidrc = findViewById(R.id.tokenidsc);
                        TextView creditidrc = findViewById(R.id.creditidsc);
                        TextView freeunitsrc = findViewById(R.id.freeunitsidsc);
                        TextView vatrc = findViewById(R.id.vatidsc);
                        TextView accountnameidrc = findViewById(R.id.namesidsc);
                        TextView address = findViewById(R.id.addressidsc);

                        dateidrc.setText(model.getPayment_date());
                        statusidrc.setText(model.getStatus());
                        meternoidrc.setText(model.getMeternumber());

                        priceidrc.setText(model.getAmount_formatted());
                        referenceidrc.setText(model.getId());
                        paymenttimeidrc.setText(model.getPayment_time());
                        tokenidrc.setText(model.getToken());
                        creditidrc.setText(String.valueOf(model.getPurchasedUnits()));
                        freeunitsrc.setText(String.valueOf(model.getFreeUnits()));
                        vatrc.setText(String.valueOf(model.getVat()));
                        accountnameidrc.setText(model.getAccount_name());
                        address.setText(model.getAddress());
                    }
                });


            }
        };

        adapter.notifyDataSetChanged();
        mTransactionList.setAdapter(adapter);
    }

    @Override
    public void onStart() {
        super.onStart();
        adapter.startListening();
    }

    @Override
    public void onStop() {
        super.onStop();
        adapter.stopListening();
    }


    public static class TransactionRecyclerHolder extends RecyclerView.ViewHolder {


        View mView;

        TransactionRecyclerHolder(View itemView) {
            super(itemView);

            mView = itemView;
        }

        public void setDate(String payment_date) {
            TextView post_date = mView.findViewById(R.id.dateid);
            post_date.setText(payment_date);
        }

        public void setStatus(String status) {
            TextView post_status = mView.findViewById(R.id.statusid);
            post_status.setText(status);
        }

        public void setMeterno(String meterno) {
            post_meterno = mView.findViewById(R.id.meternoid);
            post_meterno.setText(meterno);
        }

        public void setReference(String reference) {
            TextView post_ref = mView.findViewById(R.id.refid);
            post_ref.setText(reference);
        }

        public void setPrice(String price) {
            TextView post_price = mView.findViewById(R.id.priceid);
            post_price.setText(price);
            aamount=price;
        }


    }

    public void getUserData() {
        mAuth= FirebaseAuth.getInstance();

        try{
            userID = mAuth.getCurrentUser().getUid();
        }catch(Exception e){

            Log.d(TAG, "Failled somehow");
        }


        Log.d(TAG, "ur uid => "+userID);
        DocumentReference mDocRef = FirebaseFirestore.getInstance().collection("users").document(userID);

        mDocRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if(task.isSuccessful()){
                    DocumentSnapshot doc = task.getResult();
                    String firstname = doc.get("firstName").toString();
                    String lastname = doc.get("lastName").toString();
                    phone = doc.get("phone").toString();
                    String fullname = firstname+SPACE+lastname;
                    fullnameview.setText(fullname);

                    try{
                        emailview.setText(mAuth.getCurrentUser().getEmail());
                    }catch(Exception e){
                        Log.d(TAG,"oops");
                    }

                    charname.setText(firstname.substring(0,1));

                }
            }
        });
    }

    public void buyAgain(View view){

        final Dialog dialog = new Dialog(TransactionActivity.this);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        dialog.setCanceledOnTouchOutside(false);
        dialog.setContentView(R.layout.progressbar);

        String price = aamount.replaceAll("\D","");
       final int amount = Integer.parseInt(price) + SERVICE_CHARGE;
       final String mn = post_meterno.getText().toString();
       final String ml = "amstadam";
        final String ref = String.valueOf(Calendar.getInstance().getTimeInMillis());

        @SuppressLint("StaticFieldLeak") AsyncTask<String, String, String> jesgetnames = new AsyncTask<String, String, String>() {
            @Override
            protected String doInBackground(String... params) {

                String geMtName;
                String urlMtName = strUrlId+post_meterno.getText().toString().trim();
                Log.d(TAG, "The string:" + urlMtName);

                try {
                    URL url = new URL(urlMtName);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("GET");
                    con.connect();

                    BufferedReader bf = new BufferedReader(new InputStreamReader(con.getInputStream()));

                    String value = bf.readLine();


                    Log.d(TAG, "Value of value is: " + value);

                    if(value.equals("not found")){

                        geMtName = "NOT FOUND";

                    }else{

                        JSONObject parentJson = new JSONObject(value);
                        String meternumber = parentJson.getString("xmen");
                        String metername = parentJson.getString("x_name");
                        String address = parentJson.getString("address");
                        geMtName = metername+"/"+meternumber+"/"+address;


                    }

                    Log.d(TAG, "meter name after get is: " + geMtName);

                } catch (Exception e) {
                    // Log.d(TAG, "Faiiled coz: "+ e);
                    geMtName = "failled";
                    e.printStackTrace();
                }
                Log.d(TAG, "I dont know if this works " + geMtName);


                return geMtName;

            }


            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);

                Log.d(TAG, "pnPostExecute value: " + s);

                String nameSubString;
                String numberSubString;
                String addressSubString;


                // date and time creation
                String stringDate = String.valueOf(DateFormat.getDateTimeInstance());
                Date stringTime = Calendar.getInstance().getTime();
                SimpleDateFormat curFormater = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
                SimpleDateFormat timeFormat = new SimpleDateFormat("h:mm a", Locale.ENGLISH);
                Date dateObj = new Date();
                Date timeObj = new Date();
                try {
                    dateObj = curFormater.parse(stringDate);
                    timeObj = timeFormat.parse(String.valueOf(stringTime));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                SimpleDateFormat postFormater = new SimpleDateFormat("d MMMM yyyy", Locale.ENGLISH);
                SimpleDateFormat postTimeFormater = new SimpleDateFormat("h:mm a", Locale.ENGLISH);

                String newDateStr = postFormater.format(dateObj);
                String newtimeStr = postTimeFormater.format(timeObj);


                switch (s) {
                    case FAILLED:
                        nameSubString = FAILLED;
                        numberSubString = FAILLED;
                        addressSubString = FAILLED;
                        break;
                    case NOT_FOUND:
                        nameSubString = FAILLED;
                        numberSubString = FAILLED;
                        addressSubString = FAILLED;
                        break;
                    default:
                        String[] split = s.split("/");
                        nameSubString = split[0];
                        numberSubString = split[1];
                        addressSubString = split[2];
                        break;
                }


                switch (s) {

                    //TODO: Change toast message to reflect Json in future
                    case FAILLED:
                        //TODO: after creating layout, go back to previous layout and display toast message
                        Toast.makeText(TransactionActivity.this, COULDN_T_CONNECT_AT_THIS_TIME_PLEASE_TRY_AGAIN, Toast.LENGTH_SHORT).show();
                        dialog.dismiss();
                        break;
                    case NOT_FOUND:
                        Toast.makeText(TransactionActivity.this, PLEASE_CHECK_YOUR_METER_NUMBER_AND_TRY_AGAIN, Toast.LENGTH_SHORT).show();
                        dialog.dismiss();

                        break;
                    default:
                        Intent meterIntent = new Intent(TransactionActivity.this, MeterNumActivity.class);
                        meterIntent.putExtra("meternum", mn);
                        meterIntent.putExtra("meterprice", amount);
                        meterIntent.putExtra("meterlocation", ml);
                        meterIntent.putExtra("reference", ref);
                        meterIntent.putExtra("meteracctname", nameSubString);
                        meterIntent.putExtra("meteracctnumber", numberSubString);
                        meterIntent.putExtra("meteracctaddress", addressSubString);
                        meterIntent.putExtra("uuid", userID);
                        meterIntent.putExtra("date_created", newDateStr);
                        meterIntent.putExtra("time_created", newtimeStr);
                        meterIntent.putExtra("phone", phone);
                        meterIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(meterIntent);
                        break;
                }

            }
        };

        dialog.show();
        jesgetnames.execute();

    }

    @Override
    public void onBackPressed() {
        Intent i= new Intent(this,HomePageActivity.class);
        startActivity(i);
        finish();
    }
}

我的 Gradle 是

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.menofx.xmen"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support:cardview-v7:26.1.0'
    implementation 'com.firebaseui:firebase-ui-firestore:3.1.3'
    implementation 'com.firebaseui:firebase-ui-auth:3.1.3'
    implementation 'com.firebaseui:firebase-ui-database:3.1.3'
    implementation "com.google.firebase:firebase-firestore:11.8.0"
    implementation "com.android.support:recyclerview-v7:26.1.0"
    implementation "com.google.android.gms:play-services-auth:11.8.0"
    implementation 'co.paystack.android:paystack:3.0.9'
    implementation 'br.com.simplepass:loading-button-android:1.8.4'
    implementation 'com.github.faruktoptas:FancyShowCaseView:1.0.0'
    implementation 'com.sothree.slidinguppanel:library:3.4.0'
    implementation 'com.android.support:design:26.1.0'
    implementation 'com.android.support:support-v4:26.1.0'
    implementation 'com.android.support:support-vector-drawable:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:0.5'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
    implementation project(':library')
}

apply plugin: 'com.google.gms.google-services'

如果需要,我的 proguard 规则是空的。这是我第一次使用 firebase UI,所以我什至不知道如何诊断问题。感谢您的帮助。

您需要将您的 POJO(模型 classes)放在一个包中并将该包添加到混淆规则中,或者您可以添加这些 classes 所在的所有包(我个人只是为他们使用一个包)。 Firebase 在序列化和反序列化过程中使用反射,因此它不能使用混淆的 class 名称。一旦你将它们全部放在一个包中,将此规则添加到你的混淆文件中以防止混淆:

-keep class package.to.pojos.** { *; }

如果您希望将它们保留在原处,只需为每个模型 class 添加一个规则,其中 -keep class 关键字后跟 class 的完整路径(打包)。

您遇到问题的原因 当您将 minifyEnabled 设置为 true 时,R8 编译器通过删除不必要的参数、方法和代码的其他部分来优化您的代码,R8 认为这些部分不可用,并且还会缩小 class 名称和其他参数的名称以减少应用程序尺寸。但有时 R8 不能正确判断哪些代码有用,哪些代码没用。这就是为什么 R8 也删除了有用的代码。也许,在您的情况下,R8 更改了数据的密钥。您可以通过从已签名的应用程序中推送不同的数据来找到它。例如,在设置用户配置文件时来自用户的一些输入数据。现在返回到您的 firebase firestore 控制台并检查您刚刚推送的数据的密钥。你会发现现在按键不一样了。同样,由于 R8 在您尝试获取数据时更改了调用密钥,因此您无法找到该密钥对值。这就是为什么您的回收站视图是空的。

解决方案 1. 将以下库添加到模块的 build.gradle 文件中。

implementation 'androidx.annotation:annotation:1.1.0'
  1. 现在在所有相关的 class 处添加 @keep 注释。如果不是整个class,那么只有与推送或拉取数据有关的方法。

此致, 拉梅什