Android 应用代码可在各种设备上成功运行,包括早至 API 14 至 API 19(目标)。但是,Samsung G5 v4.4.4 在尝试为活动设置 Visibilty(true) 时会抛出 NPE。此错误可能仅在最近通过 Sprint 下载升级 G5 操作系统后才开始出现。我们已经审查了许多类型的 NPE 问题和三星特定问题,但似乎没有一个适用。
The log:
01-08 20:58:40.122: W/dalvikvm(7972): threadid=1: 线程因未捕获的异常而退出 (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):在android.app.Activity.makeVisible(Activity.java:4355)
01-08 20:58:40.142:W / System.err(7972):在android.app.Activity.setVisible(Activity.java:4336)
01-08 20:58:40.142:W / System.err(7972):在com.taskassure.app.StartTaskActivity.setActivityVisible(StartTaskActivity.java:531)
01-08 20:58:40.142: W/System.err(7972): 在 com.taskassure.app.StartTaskActivity.access$1(StartTaskActivity.java:529)
01-08 20:58:40.142:W / System.err(7972):在com.taskassure.app.StartTaskActivity $ 4.run(StartTaskActivity.java:298)
01-08 20:58:40.142: W/System.err(7972): 在 android.os.Handler.handleCallback(Handler.java:733)
01-08 20:58:40.142: W/System.err(7972): 在 android.os.Handler.dispatchMessage(Handler.java:95)
01-08 20:58:40.142:W / System.err(7972):在android.os.Looper.loop(Looper.java:146)
01-08 20:58:40.142:W / System.err(7972):在android.app.ActivityThread.main(ActivityThread.java:5678)
01-08 20:58:40.142:W / System.err(7972):在java.lang.reflect.Method.invokeNative(本机方法)
01-08 20:58:40.152:W / System.err(7972):在java.lang.reflect.Method.invoke(Method.java:515)
01-08 20:58:40.152: W/System.err(7972): 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
01-08 20:58:40.152:W / System.err(7972):在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
01-08 20:58:40.152:W / System.err(7972):在dalvik.system.NativeStart.main(本机方法)
对 StartTaskActivity 的审查确认我们正在尝试在引发异常时将可见性设置为 true。相关代码段包括:
将启动失败活动的活动 (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);
}
将启动 StartTaskActivity 的类中的相关方法
/**
* 向服务器发送启动任务的请求,获取任何检查点
* 在我们开始任务之前需要覆盖的警告。之上
* 覆盖任何警告(如果有) StartTaskActivity 已启动。
*/
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 - 在三星G5上引发NPE的活动类
/**
* 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);
}
上面的 setVisible 行是 StartTaskActivity 的第 531 行,它最终导致 Samsung G5 出现 NPE,但不会导致我们可以测试的其他设备/版本出现 NPE。正如评论中所反映的,4.4.4 模拟器上的后续测试无法复制该错误。到目前为止,仅在 4.4.4 版本的实际 Samsung G5 上观察到错误。
UPDATE:
基于一个劳动密集型调试步骤过程 https://stackoverflow.com/questions/27891436/incorrect-java-open-source-view-when-stepping-in-eclipse-with-live-device-connec通过将良好的源视图(4.4.4 模拟器)与三星不正确的源视图进行映射,我们已经缩小了 NPE 原因的范围。应用程序在调用 StartTaskActivity.setActivityVisible 时会抛出 NPE,最终会遇到 null 对象。由于跟踪过程的限制,我无法确定该对象是什么,但我猜测它是窗口或视图。引发该问题的代码行是“mDecor.setVisibility(View.VISIBLE)”(Samsung 中的第 4355 行 = Activity.java 模拟器中的第 4143 行)。因此,从技术上讲,mDecor 对象的某些部分为 null。
可能会采取不同的方法来实现我们的目标,因为我们无法确定为什么 Samsung v4.4.4 会抛出 NPE,而其他设备/模拟器(包括 Samsung v4.4.2)似乎不会抛出 NPE。也许即使这个问题尚未解决,但将来可能对其他人有用。