Android app code operates successfully on a variety of devices, including as early as API 14 and up to API 19 (target). However, Samsung G5 v4.4.4 it throws NPE when attempting to setVisibilty(true) for an activity. This error may have only started occurring after a recent upgrade of the G5 OS, via download from Sprint. We have reviewed many varieties of NPE problems and Samsung specific issues, but none seem applicable.
The log:
01-08 20:58:40.122: W/dalvikvm(7972): threadid=1: thread exiting with uncaught exception (group=0x41963da0)
01-08 20:58:40.132: W/System.err(7972): java.lang.NullPointerException
01-08 20:58:40.132: W/System.err(7972): at android.app.Activity.makeVisible(Activity.java:4355)
01-08 20:58:40.142: W/System.err(7972): at android.app.Activity.setVisible(Activity.java:4336)
01-08 20:58:40.142: W/System.err(7972): at com.taskassure.app.StartTaskActivity.setActivityVisible(StartTaskActivity.java:531)
01-08 20:58:40.142: W/System.err(7972): at com.taskassure.app.StartTaskActivity.access$1(StartTaskActivity.java:529)
01-08 20:58:40.142: W/System.err(7972): at com.taskassure.app.StartTaskActivity$4.run(StartTaskActivity.java:298)
01-08 20:58:40.142: W/System.err(7972): at android.os.Handler.handleCallback(Handler.java:733)
01-08 20:58:40.142: W/System.err(7972): at android.os.Handler.dispatchMessage(Handler.java:95)
01-08 20:58:40.142: W/System.err(7972): at android.os.Looper.loop(Looper.java:146)
01-08 20:58:40.142: W/System.err(7972): at android.app.ActivityThread.main(ActivityThread.java:5678)
01-08 20:58:40.142: W/System.err(7972): at java.lang.reflect.Method.invokeNative(Native Method)
01-08 20:58:40.152: W/System.err(7972): at java.lang.reflect.Method.invoke(Method.java:515)
01-08 20:58:40.152: W/System.err(7972): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
01-08 20:58:40.152: W/System.err(7972): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
01-08 20:58:40.152: W/System.err(7972): at dalvik.system.NativeStart.main(Native Method)
Review of StartTaskActivity confirms we are attempting to set the visibility to true when the exception is thrown. Segments of the related code include:
Activity that will launch the failing activity (StartTaskActivity):
/**
* The intent to open the task start confirm dialog. Put in globalspace so
* that data can be added to it from anywhere in this class.
*/
public intent confirmActivity = null;
/**
* Sets up the tab view showing the task details and checkpoints, as well as
* setting up the location client to get the most recent location.
*/
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.view_task_activity);
// Set up the action bar.
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
...
// initialize confirmActivity so we can add the necessary
// information from our fragments
confirmActivity = new Intent(getApplicationContext(),
StartTaskActivity.class);
}
Related methods in the class that will launch StartTaskActivity
/** * Sends a request to start the task to the server, gets back any checkpoint * warnings that need to be overridden before we can start the task. Upon * overriding any warnings (if any) The StartTaskActivity is launched. */
private void requestTaskStart()
{
...
try
{
JSONObject JsonResponse = new JSONObject(responseBody);
JSONArray checkpoints = (JSONArray) JsonResponse
.get("chekpoint_status");
JSONObject userData = new JSONObject(getIntent().getExtras()
.getString("user"));
userData = userData.getJSONObject("user");
confirmActivity.putExtra("training_set_size", new JSONObject(
getIntent().getExtras().getString("user"))
.getInt("training_set_size"));
confirmActivity.putExtra("requestStartInfo",
responseBody);
confirmActivity.putExtra("user_id",
Integer.parseInt(userData.getString("id")));
confirmActivity.putExtra("taskId", task.getInt("id"));
mDialog.dismiss();
// show checkpoint override if there are any
if ( checkpoints.length() != 0 )
{
// show first checkpoint dialog
showCheckpointDialog(checkpoints, 0);
}
else
{
startActivityForResult(confirmActivity,
CONFIRM_TASK_START);
}
StartTaskActivity - the activity class that throws NPE on Samsung G5
/**
* Activity that is shown after choosing to start a task. Shows the confirmation
* window before a task is started. This activity also handles starting face
* verification if necessary.
*/
public class StartTaskActivity extends Activity
{
...
/**
* Creates the task confirm screen, downloads the users photo from the server.
* Checks the task checkpoints to see if face verification needs to be done
* before starting the task. Keeps the activity invisible until all
* checkpoints are properly met.
*/
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.start_task_confirm_layout);
Intent launchIntent = getIntent();
Bundle args = launchIntent.getExtras();
try
{
requestTaskStartData = new JSONObject(args.getString("requestStartInfo"));
taskCheckpoints = new JSONArray(args.getString("checkpoints"));
taskId = args.getInt("taskId");
((TextView) findViewById(R.id.task_confirm_textview))
.setText(requestTaskStartData.getString("task_summary"));
new Thread(new Runnable()
{
@Override
public void run()
{
...
// code retrieves an image file from server on separate thread
// depending on results, call checkVerifyIdentity for additional processing and to show view
...
checkVerifyIdentity(bmp)
}).start();
((TextView) findViewById(R.id.task_password_content_textview))
.setText(requestTaskStartData.getString("task_password"));
}
catch ( JSONException e )
{
((TextView) findViewById(R.id.task_password_title_textview))
.setVisibility(TextView.INVISIBLE);
e.printStackTrace();
}
findViewById(R.id.task_confirm_button).setOnClickListener(
new View.OnClickListener()
{
@Override
public void onClick(View v)
{
setResult(RESULT_OK);
finish();
}
});
findViewById(R.id.task_deny_button).setOnClickListener(
new View.OnClickListener()
{
@Override
public void onClick(View v)
{
setResult(RESULT_CANCELED);
finish();
}
});
} // end of StartTaskActivity.onCreate
...
// Check some parameters, and finish setting up view for display (runs on UI thread)
private void checkVerifyIdentity(final Bitmap bmp)
{
final Context context = this;
StartTaskActivity.this.runOnUiThread(new Runnable()
{
public void run()
{
if ( bmp != null )
{
((ImageView) findViewById(R.id.task_confirm_imageview))
.setImageBitmap(bmp);
}
if ( taskCheckpoints.length() > 0 )
{
... // do some processing
}
else
{
setActivityVisible();
}
}
});
}
...
/**
* Sets the activity as visible. Should be called once all verifications are
* properly checked.
*/
private void setActivityVisible()
{
this.setVisible(true);
}
The above line to setVisible is line 531 of StartTaskActivity which eventually causes the NPE for Samsung G5, but not other devices/versions that we can test. As reflected in comments, subsequent test on 4.4.4 emulator is not able to replicate the error. So far, error only observed on actual Samsung G5 with 4.4.4.
UPDATE:
Based on a labor intensive debug step process of mapping a good source view (4.4.4 emulator) with the Samsung incorrect source view, we have narrowed in on the cause of the NPE. The app throws an NPE when it calls StartTaskActivity.setActivityVisible, which eventually runs into a null object. Because of the limits to the tracing process, I can’t say with certainty what that object is, but my guess is that it is the window, or view. The line of code that throws it is “mDecor.setVisibility(View.VISIBLE)” (line 4355 in Samsung = line 4143 in Emulator of Activity.java). So, technically, some part of the mDecor object is null.
Will probably take a different approach to achieve our goal, since we cannot determine why the Samsung v4.4.4 throws NPE when no other device/emulator seems to, including Samsung v4.4.2. Perhaps even though this question is unresolved, it might be useful to others in the future.
Significant time was spent attempting to isolate the cause of this issue. We have not been able to reproduce the NPE exception on any other device or emulator except the Samsung G5 (Sprint) running 4.4.4. The crash occurs when attempting to use the setVisible method on an activity that had a background thread launched from within onCreate, which then launches a runsOnUiThread. Perhaps this was an improper practice that for some reason wasn't bothered by other systems, but the Samsung (at least) found it unacceptable.
In any case, we resolved this by showing/hiding the view associated with the activity (using for example
v.setVisibility(View.VISIBLE)
and some worthwhile refactoring. As indicated by the Android documentation, use Activity#setVisible method with caution.