Key words: NullPointerException / BaseActivity caused by force / home key / static
Background: Static variables are often used in Android programming. Static variables belong to the class itself. The values of static variables invoked by all instances are the same. If the values of a static variable are changed in one class, all other instances will change when the values are invoked. Static takes up memory separately in the virtual machine and can be used in different packages and classes, which is very convenient. But when the application is killed, if the application is in the background for a long time, it will lead to the exception of NullPointerException.
Interpretation: Because by pressing the home key of Android, if the application is in the background for a long time, or if the application is forced to kill because of insufficient memory, the application will still keep the activity stack information (the activity stack is not empty, for example, A-> B-> C-> D stack is still saved, but the ABCD instances are not. So when we go back to App, it's still the D page. When we choose the "recently opened application" to go back to the front desk, the activity will re-execute onCreate() for initialization (including application initialization), if the operation contains references to static variables of other classes, and the instance of the static variable after the application is killed has been virtual machine. Recycle, which causes null pointers.
So the problem is, we should go back to the application process, how to improve this situation and avoid this abnormal occurrence. Since App has been killed, why not go back to the first start process, don't let App go back to D, but start A again, so that all variables are initialized according to the normal process, and there will be no empty pointer. You need to decide whether you were killed or not, and if so, force you to go back to the start of the application process. Through intentional classroom learning, this process is simulated and the ideas are sorted out.
Reference resources: What to do if an application is forced to kill at http://notes.stay4it.com/2016/02/26/how-to-handle-app-force-killed/
Process carding#
Custom Application is customized to initialize global variables
public class CustomApplication extends Application {
public static ArrayList<String> mTestNullPointer;
// Let's simulate - 1 as being killed by force.
public static int mAppStatus = -1;
@Override
public void onCreate() {
super.onCreate();
}
}
A parent class BaseActivity is made to inherit each Activity from BaseActivity. Each Activity will either execute the protectApp() method or the setupData() method. The former is due to force killing, and the latter is the normal initialization operation.
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Judge that if you are murdered, go back to HomeActivity, otherwise you can initialize
if (CustomApplication.mAppStatus == -1) {
// Return to application process
protectApp();
} else {
setupData();
}
}
// Do initialization here
protected void setupData() {
}
// protected allows subclasses to override this method
protected void protectApp() {
// Removing the application process is the right thing to do, because it's unreasonable to force an application to kill and save Activity stack information.
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra("action", "force_kill");
startActivity(intent);
}
}
Here's how to simulate assassination and optimize it.
The corresponding code for each Activity in the figure is as follows
1,WelcomeActivity
public class WelcomeActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Turning the state to zero prevents the parent class from walking protectApp()
CustomApplication.mAppStatus = 0;
super.onCreate(savedInstanceState);
}
@Override
protected void setupData() {
setContentView(R.layout.activity_welcome);
handler.sendEmptyMessageDelayed(0, 1000);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
finish();
}
};
}
2,LoginActivity
public class LoginActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
public void login(View view) {
startActivity(new Intent(this, HomeActivity.class));
}
}
3,HomeActivity
/**
* HomeActivity Startup mode is "singleTask"
*/
public class HomeActivity extends BaseActivity implements View.OnClickListener {
private Button mHomeProfileBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
// Initialization operation
@Override
protected void setupData() {
setContentView(R.layout.activity_home);
mHomeProfileBtn = $(R.id.id_mHomeProfileBtn);
mHomeProfileBtn.setOnClickListener(this);
CustomApplication.mTestNullPointer = new ArrayList<>();
CustomApplication.mTestNullPointer.add("profile");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getStringExtra("action");
if ("force_kill".equals(action)) {
// Be killed in ProfileActivity and go back to the application process
protectApp();
}
}
@Override
protected void protectApp() {
// Back to WelcomeActivity
startActivity(new Intent(this, WelcomeActivity.class));
finish();
}
@Override
public void onClick(View view) {
startActivity(new Intent(this, ProfileActivity.class));
}
@SuppressWarnings("unchecked")
private <T> T $(int resId) {
return (T) findViewById(resId);
}
}
4,ProfileActivity
public class ProfileActivity extends BaseActivity {
private TextView mProfileLabel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Let all the onCreate() subclasses inherited from BaseActivity be handed over directly to the parent BaseActivity to operate on.
// Let the parent class make a series of first judgments and not let the child class initialize casually.
}
@Override
protected void setupData() {
setContentView(R.layout.activity_profile);
mProfileLabel = $(R.id.id_mProfileLabel);
mProfileLabel.setText(CustomApplication.mTestNullPointer.toString());
}
@SuppressWarnings("unchecked")
private <TT> TT $(int resId) {
return (TT) findViewById(resId);
}
}
End.
Note by HF.
Learn from Intentional Classroom