android:立即启动一个活动,然后加载内容
Posted
技术标签:
【中文标题】android:立即启动一个活动,然后加载内容【英文标题】:android: Instantly start an activity and then load content 【发布时间】:2016-07-22 23:36:15 【问题描述】:我查看了其他问题、博客和文档,但似乎无法找到满足我需求的正确答案。 我有两个活动,A 和 B。当我(从 A)开始活动 B 时,我希望它立即打开,然后在显示进度条的同时加载所有内容,而不是仅在加载内容时打开活动,使其仿佛凝固了两秒。 Youtube 应用或 Play 商店就是一个例子。
这就是我得到的:
Button.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent goB = new intent(ActivityA.this, ActivityB.class);
startActivity(goB);
);
这是我正在加载的活动:
public class ActivityB extends AppCompatActivity implements OnDateSelectedListener, OnMonthChangedListener
private static final DateFormat FORMATTER = SimpleDateFormat.getDateInstance();
@Bind(R.id.calendarView) MaterialCalendarView widget;
@Bind(R.id.textView) TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c_calendar);
ButterKnife.bind(this);
widget.setOnDateChangedListener(this);
widget.setOnMonthChangedListener(this);
textView.setText(getSelectedDatesString());
@Override
public void onDateSelected(@NonNull MaterialCalendarView widget, @Nullable CalendarDay date, boolean selected)
textView.setText(getSelectedDatesString());
private String getSelectedDatesString()
CalendarDay date = widget.getSelectedDate();
if (date == null)
return "No Selection";
return FORMATTER.format(date.getDate());
@Override
public void onMonthChanged(MaterialCalendarView widget, CalendarDay date)
我不是专家,欢迎详细解释。
注意:我在活动中加载的是这个日历:https://github.com/prolificinteractive/material-calendarview
问题:如何在后台加载setContentView()
?
更新:我听从了 Hitesh Sahu 的建议,现在我只有一个带有一个容器的活动,每个片段都会被替换,我假设在后台加载 xml 内容的方式是片段和活动相同,但如果有任何区别,请务必提及。
【问题讨论】:
"当我(从 A)开始活动 B 时,我希望它立即开始,然后在显示进度条的同时加载所有内容,而不是在单击按钮后延迟让它看起来像活动愣了一秒。” 当你开始一个活动时,它会默认立即开始 这不是默认情况下发生的情况,我的第二个活动在其中实现了一个沉重的库,当我单击按钮启动该活动时,它大约需要一秒钟才能打开。我希望它立即打开,然后加载所有内容,而不是仅在加载内容时打开它 如果您正在执行后台任务,则在您的 B 活动中使用 AsyncTask 类 你能帮我做吗? 【参考方案1】:可能的解决方案
根据“我不是专家”,我的猜测是您可能会在每次屏幕切换时创建新活动,并且可能不会清除旧活动或重用现有活动。这也是我的猜测,我没有看到你的代码,但我看到开发人员犯了这种错误。很快你的应用程序的性能会随着时间的推移而下降,你的应用程序最终会吃掉内存。
如何解决这个问题:- 将 activities 替换为 fragment 切换片段比切换 Activity 相对更快,而且您可以重复使用它们 (@987654321 @)。
您可能正在主 Ui 线程上执行一些繁重的任务,查找 choreographer 日志,例如 - skipped n number of frames app may be doing too much work on main thread
如何解决这个问题:- 已经在其他答案中解释过。
【讨论】:
是的,你猜对了,这可能是没有人提到的基本优化之一,因为它太明显了,所以谢谢你这样做。【参考方案2】:您的活动 B 应该有 AsyncTask 用于加载数据然后显示它。
你也可以在OnPreExecute()
的视图中加载一些默认数据
@Override
protected void onCreate(Bundle savedInstanceState)
setupViews();
private void setupViews()
someView1 = (TextView) findViewById(R.id.some_view_id1);
someView2 = (TextView) findViewById(R.id.some_view_id2);
someView3 = (ImageView) findViewById(R.id.some_view_id3);
progressBar = (ProgressBar) findViewById(R.id.progress_bar_id);
new LoadDataForActivity().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
private class LoadDataForActivity extends AsyncTask<Void, Void, Void>
String data1;
String data2;
Bitmap data3;
@Override
protected void onPreExecute()
progressBar.setVisibility(View.VISIBLE);
@Override
protected Void doInBackground(Void... params)
data1 = loadData1();
data2 = loadData2();
data3 = loadData3();
@Override
protected void onPostExecute(Void result)
someView1.setText(data1);
someView2.setText(data2);
someView3.setImageBitmap(data3);
progressBar.setVisibility(View.GONE);
【讨论】:
它不起作用,我添加了Looper.prepare();
并将 null 返回到 doInbackground
并且它没有被调用,我在其中放了一个 toast 来检查
尝试在 OnCreate 方法之后发布 AsyncTask 执行,例如 runOnUiThread(new Runnable() @Override public void run() new LoadDataForActivity().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);)跨度>
【参考方案3】:
您需要使用AsyncTask
加载数据并使用RelativeLayout
在ActivityB
中显示圆角。
首先初始化变量
private RelativeLayout mRelativeLayout;
并在您的活动的onCreate
中像这样执行AsyncTask
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRelativeLayout = (RelativeLayout) findViewbyID(R.id.loadingCircle);
new PrepareData().execute();
将此AsyncTask
类放入ActivityB
private class PrepareData extends AsyncTask<Void, Void, Void>
protected void onPreExecute(Void param)
// THIS WILL DISPLAY THE PROGRESS CIRCLE
mRelativeLayout.setVisibility(View.VISIBLE)
protected Void doInBackground(Void... param)
// PUT YOUR CODE HERE TO LOAD DATA
return null;
protected void onPostExecute(Void param)
// THIS WILL DISMISS CIRCLE
mRelativeLayout.setVisibility(View.GONE)
在XML
中包含此布局
为了得到ProgressBar
不带对话框就是用这个
<RelativeLayout
android:id="@+id/loadingCircle"
android:layout_
android:layout_
android:visibility="gone"
android:gravity="center" >
<ProgressBar
android:layout_
android:layout_
android:indeterminate="true" />
</RelativeLayout>
并分别在onPreExecute
和onPostExecute
中设置此RelativeLayout
的可见性,就像我在示例中显示的那样。
更新:
您需要像这样在UiThread
中更新您的Ui
runOnUiThread(new Runnable()
@Override
public void run()
//put the code here that is giving exception
);
【讨论】:
你能更新一下进度条吗?还是以同样的方式使用? 我已经包含了ProgressDialog
的方法,它会在活动开始时显示进度对话框(移动圆圈),一旦你完成加载数据就会关闭它,你需要输入加载数据的代码在我上面提到的doInBackground
方法中
是的,我知道,我的意思是ProgressBar,至于Dialog外的圆形进度条,就是在activity中,打开youtube app或者instagram app就能明白我的意思
这几乎奏效了,我将所有内容从 onCreate
移动到 doInBackground
,包括 setContentView(...)
,因为昂贵的是日历加载,它立即打开了活动,但日历准备好了显示它崩溃了。这是错误:java.lang.RuntimeException: An error occured while executing doInBackground()
和 Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
不,不要在后台移动setContentView(R.layout.activity_main);
,按照我向您展示的示例尝试【参考方案4】:
您可以使用 AsyncTask。
例如:在你的活动页面中添加这个(在 onCreate() 之外)。
class OnLoading extends AsyncTask<Void,Void,Void>
ProgressDialog pd;
@Override
protected void onPreExecute()
super.onPreExecute();
pd=new ProgressDialog(Main2Activity.this);
pd.setTitle("Title");
pd.setMessage("Message");
pd.setCancelable(false);
pd.show();
@Override
protected Void doInBackground(Void... params)
//
//Do all your loading stuff here
//
return null;
@Override
protected void onPostExecute(Void aVoid)
super.onPostExecute(aVoid);
pd.dismiss();
@Override
protected void onProgressUpdate(Void... values)
super.onProgressUpdate(values);
这个类是通过调用来执行的
new OnLoading().execute();
在执行此代码时,执行的第一个函数将是 onPreExecute->doInBackGround->onPostExecute.。进度更新可用于显示 doInBackGround 函数的一些进度,方法是调用
publishProgress(value);
这里首先会显示 ProgressDialog。提供代码以在 doInBackground 中加载数据等。顾名思义,您在此函数中编写的任何内容都将在后台执行。此函数执行完成后,进度对话框将在 onPostExecute() 中关闭。
【讨论】:
doInBackGround 中的参数是否应为String... params
?
我必须加载的重物在布局上,我不能将 setContentView(layout) 放在异步中的任何位置【参考方案5】:
听起来您正在 onCreate
函数中做所有事情(包括繁重的负载)。查看Activity life cycle 并考虑将代码放入例如onStart
或 onResume
。此外,例如,可以将重物放在worker thread 上。这个keeps your UI responsive和用户更开心了。
【讨论】:
我在第二个活动中实现了一个日历库,它所做的只是在 TextView 中显示选定的日期,它的外观如下:imgur.com/b3ZmLRd【参考方案6】:首先开始你的活动
startActivity(new intent(A.this, B.class));
从您选择的任何事件中,您的活动 B 应该如何工作。
class B extends AppCompatActivity implements OnDateSelectedListener, OnMonthChangedListener
private static final DateFormat FORMATTER = SimpleDateFormat.getDateInstance();
@Bind(R.id.calendarView) MaterialCalendarView widget;
@Bind(R.id.textView) TextView textView;
ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
if(!B.this.isFinishing() && progressDialog != null && !progressDialog.isShowing())
progressDialog.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
@Override
public void run()
ButterKnife.bind(B.this);
widget.setOnDateChangedListener(B.this);
widget.setOnMonthChangedListener(B.this);
textView.setText(getSelectedDatesString());
if(!B.this.isFinishing() && progressDialog != null && progressDialog.isShowing())
progressDialog.hide();
, 1500);
@Override
public void onDateSelected(@NonNull MaterialCalendarView widget, @Nullable CalendarDay date, boolean selected)
textView.setText(getSelectedDatesString());
private String getSelectedDatesString()
CalendarDay date = widget.getSelectedDate();
if (date == null)
return "No Selection";
return FORMATTER.format(date.getDate());
@Override
public void onMonthChanged(MaterialCalendarView widget, CalendarDay date)
编辑:正如@Andre99 使用进度条显示的那样:
-在 B 布局中添加 ProgressBar
-在 B 的 onCreate 中获取对这个 ProgressBar 的引用(使用 findViewById()
-每次都用 setProgress(int) 更新 ProgressBar(如 ProgressDialog 问题的第一部分所示)
-使用 yourProgressBar.setVisibility(View.GONE) 隐藏你的 ProgressBar
【讨论】:
我无法将我正在加载的内容放入doInBackground
,请查看已编辑的问题,我添加了我正在打开的活动
试试这个编辑过的代码。我相信这应该有效。是的,这应该与进度条相同。 Andre99 上面的回答展示了如何使用进度条。
doinBackground widget.setOnDateChangedListener(Activity_c_Calendar.this);
中的这些行给出错误“必须从 UI 线程调用方法 setOnDateChangedListener,当前推断线程是工作线程”,ButterKnife.bind(B.this)
也没有给出错误但没有被调用因为我在 TextView 中得到了一个空点
另一种方法是使用处理程序并在可运行的 postDelayed() 中执行这些任务。我再次编辑了我的代码。
什么都没有发生,我把定时器改成5s它没有运行它,activity加载后打开,大约1s【参考方案7】:
只需输入以下代码:
Intent goB = new intent(ActivityA.this, ActivityB.class);
startActivity(goB);
在 A 的 onCreate 中。然后创建一个 ProgressDialog,如下所示:http://www.tutorialspoint.com/android/android_progressbar.htm
这将显示B加载的进度。你会打电话给
progress.setProgress(int)
在B的onCreate中,做了某事之后。
例如:
public class B extends Activity
public void onCreate(Bundle bundle)
progress=new ProgressDialog(this);
progress.setMessage("Downloading Music");
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setIndeterminate(true);
progress.setProgress(0);
progress.show();
//do something
progress.setProgress(1);
//do something
progress.setProgress(2);
//do something different
progress.setProgress(3);
.....
编辑
以下是使用 ProgressBar 的方法:
在 B 布局中添加ProgressBar
在 B 的onCreate
中获得对这个ProgressBar
的引用(使用findViewById()
每次你想用setProgress(int)
更新你的ProgressBar
(如ProgressDialog
问题的第一部分所示)
使用您的ProgressBar.setVisibility(View.GONE)
隐藏您的ProgressBar
【讨论】:
这似乎是一个不错的方法,但我不是在寻找 ProgressDialog,它是否也适用于 ProgressBar? 可以,你可以在B活动布局中放一个ProgressBar 好的,我该如何使用 ProgressBar? 好的,现在我如何告诉 ProgressBar 在所有内容加载后消失?要加载的所有内容都在 xml 文件中,代码中没有任何内容 而且activity还是在layout加载后才打开,我需要在ProgressBar可见的时候立即打开然后加载【参考方案8】:对于不需要立即显示的所有内容,您应该使用ViewStub。从 XML 布局中膨胀视图可能是一项非常昂贵的操作,尤其是在您有多个嵌套视图组的情况下。 ViewStub 的工作原理是将 不必要的布局膨胀从 onCreate() 偏移到您选择的位置,这意味着您的活动更快地出现在屏幕上。
<LinearLayout
android:layout_
android:layout_
android:orientation="vertical">
<TextView
android:id="@+id/textview"
android:layout_
android:layout_/>
<ProgressBar
android:id="@+id/progressbar"
android:layout_
android:indeterminate="true"
android:layout_/>
<ViewStub
android:id="@+id/view_stub"
android:inflatedId="@+id/calendarView"
android:layout="@layout/my_expensive_layout"
android:layout_
android:layout_ />
</LinearLayout>
现在 ProgressBar 显示在屏幕上,我们可以在 onCreate() 之后的生命周期调用中为 ViewStub 充气,例如 onPostCreate() 或 onResume()。您可以使用以下方法实现:
viewStub.setOnInflateListener(new OnInflateListener()
void onInflate(ViewStub stub, View inflated)
calendarView = (MaterialCalendarView) inflated.findViewById(R.id.calendarView);
// perform any calendar setup here
progressBar.setVisibility(View.GONE); // hide progressbar, no longer needed
);
viewStub.inflate();
绝对值得检查您的视图层次结构,并在可能的情况下简化任何嵌套的视图组,因为这确实会在视图性能方面造成影响。如果您的整个应用程序运行缓慢,则可能表明存在更大的问题,例如内存泄漏,您可以使用 LeakCanary 检测到。
【讨论】:
这似乎是一个不错的解决方案,感谢图书馆。my_expensive_layout
tho 是什么?
my_expensive_layout 将是一个单独的布局文件。如果您只有一个 MaterialCalendarView,它只会包含它。
好的,所以我创建了一个 my_expensive_layout 并在其中添加了日历,所以我不得不从 ViewStub 中删除 android:inflatedId="@+id/calendarView"
并且它不起作用,只有在加载内容时布局才会打开跨度>
【参考方案9】:
你可以试试下面的代码:
Intent intent= new Intent(currentActivity.this, targetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
希望,它会正常工作。祝你好运。
【讨论】:
这感觉就像打开一个新应用程序,当我单击“返回”按钮时它甚至会关闭应用程序,尽管它在加载之前确实打开了,但我会尝试其他标志跨度>以上是关于android:立即启动一个活动,然后加载内容的主要内容,如果未能解决你的问题,请参考以下文章