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 加载数据并使用RelativeLayoutActivityB 中显示圆角。

首先初始化变量

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>

并分别在onPreExecuteonPostExecute 中设置此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 并考虑将代码放入例如onStartonResume。此外,例如,可以将重物放在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_layouttho 是什么? 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:立即启动一个活动,然后加载内容的主要内容,如果未能解决你的问题,请参考以下文章

谁能解释一下为啥这个 android 布局不工作并且在活动启动后立即使活动崩溃

Android,完成所有活动

Android:如何在启动画面上加载主要活动

MainActivity 运行时未启动 [重复]

Android:线程从网络加载数据后更新Listview

设置内容视图();在我的启动画面活动中出错