當前位置:首頁 » 安卓系統 » androidlistview分頁載入

androidlistview分頁載入

發布時間: 2025-03-31 23:15:49

❶ android中listview的下拉刷新上拉載入是怎麼實現的

這是兩個分開的部分。如果你是新手,先一個一個來。

我只能跟你說一下思路,具體的東西你在網上查查,不行再問我,新手的話慢慢來。

  1. 下拉刷新,獲取listview的下拉時間顯示header,然後調用更新數據的介面就可以了。

  2. 上啦載入,是分頁獲取數據,獲取listview的是否拉到最底,如果拉倒最底,獲取數據,讓後list的數據添加獲取的數據,更新adapter就可以了。


❷ android軟體開發怎樣實現分頁功能

ListView分頁:
(一)、目的:
Android 應用開發中,採用ListView組件來展示數據是很常用的功能,當一個應用要展現很多的數據時,一般情況下都不會把所有的數據一次就展示出來,而是通過 分頁的形式來展示數據,這樣會有更好的用戶體驗。因此,很多應用都是採用分批次載入的形式來獲取用戶所需的數據。例如:微博客戶端可能會在用戶滑 動至列表底端時自動載入下一頁數據,也可能在底部放置一個"查看更多"按鈕,用戶點擊後,載入下一頁數據。
(二)、核心技術點:
藉助 ListView組件的OnScrollListener監聽事件,去判斷何時該載入新數據;
往伺服器get傳遞表示頁碼的參數:page。而該page會每載入一屏數據後自動加一;
利用addAll()方法不斷往list集合末端添加新數據,使得適配器的數據源每新載入一屏數據就發生變化;
利用適配器對象的notifyDataSetChanged()方法。該方法的作用是通知適配器自己及與該數據有關的view,數據已經發生變動,要刷新自己、更新數據。

(三)、 OnScrollListener監聽事件 :
1、該監聽器中有兩個需要實現的方法:
onScrollStateChanged(AbsListView view, int scrollState):監聽屏幕的滾動狀態的變動情況
onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount):監聽屏幕滾動的item的數量
2、 scrollState 回調順序如下:
第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1):表示正在滾動。當屏幕滾動且用戶使用的觸碰或手指還在屏幕上時為1
第2次:scrollState =SCROLL_STATE_FLING(2) :表示手指做了拋的動作(手指離開屏幕前,用力滑了一下,屏幕產生慣性滑動)。
第3次:scrollState =SCROLL_STATE_IDLE(0) :表示屏幕已停止。屏幕停止滾動時為0。
3、 onScroll中參數講解:
firstVisibleItem:當前窗口中能看見的第一個列表項ID(從0開始)
visibleItemCount:當前窗口中能看見的列表項的個數(小半個也算)
totalItemCount:列表項的總數
4、思路:
當滾到最後一條,載入新數據;
適配器的數據源要進行累加:totalList.addAll(list);
數據發生變化,適配器通知:adapter.notifyDataSetChanged();【牢記】
判斷是否滾到最後一行。
(五)、核心代碼:

1、布局文件的核心代碼:

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ListView
android:id="@+id/listView_main"
android:layout_below="@+id/button_main_init"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>

<LinearLayout
android:id="@+id/layout_main_nextpage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#000"
android:visibility="invisible"
android:gravity="center"
android:onClick="clickButton"
android:padding="5dp">

<ProgressBar
android:id="@+id/progressBar_main"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/text_main_nextpage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textSize="18sp"
android:onClick="clickButton"
android:textColor="#fff"
android:text="點擊載入更多數據"/>
</LinearLayout>

</RelativeLayout>
2、Activity頁面核心代碼:

publicclass MainActivity extends Activity {
privateStringTAG= "MainActivity";
privateListView listView_main;
privateLinearLayout layout_main_nextpage;

private MysqliteDatabaseHelper dbHelper = null;

// 用於分頁顯示數據的屬性
privateintpageSize= 30;// 每頁顯示的條數
privateintcurPage= 1;
privateintrowCount= 0;
privateintpageCount= 0;// 總頁數

privatebooleanisBottom=false;// 判斷是否滾動到數據最後一條
private List<Map<String, Object>> totalList = null;// 載入到適配器中的數據源
private SimpleAdapter adapter = null;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

listView_main = (ListView) findViewById(R.id.listView_main);
layout_main_nextpage = (LinearLayout) findViewById(R.id.layout_main_nextpage);

// 實例化訪問資料庫幫助類
dbHelper = new MySQLiteDatabaseHelper();
// 獲取數據表一共有多少條,從而計算共有多少頁
rowCount=dbHelper.selectCount("select id from android_basic",null);
// 計算總頁碼數
pageCount = (int) Math.ceil(rowCount / (float) pageSize);

// 如果當前頁為第一頁,則數據源集合中就是第一頁的內容
if (curPage == 1) {
totalList = getCurpageList(1);
}
adapter = new SimpleAdapter(this, totalList,
R.layout.item_listview_main, new String[] { "_id", "title" },
newint[] { R.id.text_item_listview_id,
R.id.text_item_listview_title});
listView_main.setAdapter(adapter);

// 給ListView對象設置滾動監聽器,以此來判斷是否已經滾動到最後一條,從而決定是否載入新數據
listView_main.setOnScrollListener(new OnScrollListener() {
@Override
publicvoid onScrollStateChanged(AbsListView view, int scrollState) {
if (isBottom) {
// 如果滾到最後一條數據(即:屏幕最底端),則顯示:「載入更多新數據」
if(curPage < pageCount) {
layout_main_nextpage.setVisibility(View.VISIBLE);
}
} else {
layout_main_nextpage.setVisibility(View.GONE);
}
}

@Override
publicvoid onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// Log.i(TAG, "==" + firstVisibleItem + ":::" + visibleItemCount
// + ":::" + totalItemCount);
// 判斷是否已經滾動到了最後一條,從而決定是否提示載入新數據
isBottom = (firstVisibleItem + visibleItemCount == totalItemCount);
}
});
}

publicvoid clickButton(View view) {
switch (view.getId()) {
caseR.id.layout_main_nextpage:
// Log.i(TAG, "==" + curPage + ":::" + pageCount);
// 如果不是最後一頁,則讓當前頁碼累加,讓數據源累加新數據,並通知適配器信息發生變化
if(curPage < pageCount) {
curPage++;
totalList.addAll(getCurpageList(curPage));
adapter.notifyDataSetChanged();
}
// 只要點擊了提示「載入新數據」的信息,就讓其隱藏
layout_main_nextpage.setVisibility(View.GONE);
break;
default:
break;
}
}

// 獲取每一頁的數據,返回List集合
private List<Map<String, Object>> getCurpageList(int currentPage) {
int offset = (currentPage - 1) * pageSize;
String sql = "select id _id ,title from android_basic limit ? , ?";
returndbHelper.selectData(sql, new String[] { offset + "",
pageSize + "" });
}

}

❸ android中怎麼實現上拉刷新

這篇文章主要介紹了android實現listview下拉刷新和上拉刷新效果,Android的ListView上拉下拉刷新,原理都一樣,在Touch事件中操作header/footer的paddingTop屬性,需要的朋友可以參考下

java">{

privatestaticfinalStringTAG=PullToLoadListView.class.getSimpleName();

privatestaticfinalintSTATE_NON=0;
privatestaticfinalintSTATE_PULL_TO_REFRESH=1;
privatestaticfinalintSTATE_RELEASE_TO_REFRESH=2;
privatestaticfinalintSTATE_REFRESHING=3;

privateintstate;

privateintfirstVisibleItem;
privateintlastVisisibleItem;

privatefloatprevY=0;

privateViewheaderView;
privateViewfooterView;

//headerwidgets
;
;
privateTextViewheaderText;
;
;
//footerwidgets
;
privateTextViewfooterText;

privatebooleanheaderIsHanding=false;
privatebooleanfooterIsHanding=false;

privateintheaderHeight;
privateintfooterHeight;

;

;

;

publicPullToLoadListView(Contextcontext){
super(context);
init(context);
}

publicPullToLoadListView(Contextcontext,AttributeSetattrs){
super(context,attrs);
init(context);
}

privatevoidinit(Contextcontext){
state=STATE_NON;
firstVisibleItem=0;
lastVisisibleItem=0;

LayoutInflaterinflater=LayoutInflater.from(context);
headerView=inflater.inflate(R.layout.view_pull_header,null);
footerView=inflater.inflate(R.layout.view_pull_footer,null);

headerProgressBar=(ProgressBar)headerView.findViewById(R.id.progressbar);
headerImageArrow=(ImageView)headerView.findViewById(R.id.arrow);
headerText=(TextView)headerView.findViewById(R.id.text);
headerArrowAnim=newRotateAnimation(0,-180,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
headerArrowAnim.setDuration(300);
headerArrowAnim.setFillAfter(true);
headerArrowReverseAnim=newRotateAnimation(-180,0,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
headerArrowReverseAnim.setDuration(300);
headerArrowReverseAnim.setFillAfter(true);

footerProgressBar=(ProgressBar)footerView.findViewById(R.id.progressbar);
footerText=(TextView)footerView.findViewById(R.id.text);

measureView(headerView);
measureView(footerView);
headerHeight=headerView.getMeasuredHeight();
footerHeight=footerView.getMeasuredHeight();
headerView.setPadding(0,-1*headerView.getMeasuredHeight(),0,0);
footerView.setPadding(0,-1*footerView.getMeasuredHeight(),0,0);
headerView.invalidate();
footerView.invalidate();
addHeaderView(headerView,null,false);
addFooterView(footerView,null,false);

super.setOnScrollListener(this);
}

privatevoidmeasureView(Viewview){
ViewGroup.LayoutParamslp=view.getLayoutParams();
if(lp==null){
lp=newViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
intchildWidthSpec=ViewGroup.getChildMeasureSpec(0,0,lp.width);
intchildHeightSpec;
if(lp.height>0){
childHeightSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.EXACTLY);
}else{
childHeightSpec=MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
}
view.measure(childWidthSpec,childHeightSpec);
}

privatevoidresetHeader(){
//headerView.setPadding(0,-1*headerHeight,0,0);
resetAnim=newResetAnimation(headerView,headerHeight,headerView.getPaddingTop());
resetAnim.start();
}

privatevoidresetFooter(){
resetAnim=newResetAnimation(footerView,footerHeight,footerView.getPaddingTop());
resetAnim.start();
}

(intstate){
if(this.state==state){
return;
}
intprevState=this.state;
this.state=state;

switch(state){
caseSTATE_NON:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerImageArrow.clearAnimation();
headerText.setText("PullDownToRefresh");
break;
caseSTATE_PULL_TO_REFRESH:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerText.setText("PullDownToRefresh");
if(prevState==STATE_RELEASE_TO_REFRESH){
headerImageArrow.startAnimation(headerArrowReverseAnim);
}else{
headerImageArrow.clearAnimation();
}
break;
caseSTATE_RELEASE_TO_REFRESH:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerImageArrow.startAnimation(headerArrowAnim);
headerText.setText("ReleaseToRefresh");
break;
caseSTATE_REFRESHING:
headerProgressBar.setVisibility(View.VISIBLE);
headerImageArrow.setVisibility(View.INVISIBLE);
headerImageArrow.clearAnimation();
headerText.setText("Refreshing");
break;
default:
break;
}
}

(intstate){
if(this.state==state){
return;
}
this.state=state;

switch(state){
caseSTATE_NON:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("PullUpToRefresh");
break;
caseSTATE_PULL_TO_REFRESH:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("PullUpToRefresh");
break;
caseSTATE_RELEASE_TO_REFRESH:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("ReleaseToRefresh");
break;
caseSTATE_REFRESHING:
footerProgressBar.setVisibility(View.VISIBLE);
footerText.setText("Refreshing");
break;
default:
break;
}
}

@Override
publicvoidsetOnScrollListener(OnScrollListenerl){
this.onScrollListener=l;
}

(){
this.onLoadingListener=onLoadingListener;
}

publicvoidloadCompleted(){
if(headerIsHanding){
changeHeaderViewByState(STATE_NON);
resetHeader();
headerIsHanding=false;
}
if(footerIsHanding){
changeFooterViewByState(STATE_NON);
resetFooter();
footerIsHanding=false;
}
}

(MotionEventev){
headerIsHanding=true;
floattempY=ev.getRawY();
floatvector=tempY-prevY;
vector/=2;
prevY=tempY;
if(vector>0){
intnewPadding=(int)(headerView.getPaddingTop()+vector);
newPadding=Math.min(newPadding,headerHeight/2);
headerView.setPadding(0,newPadding,0,0);
if(state!=STATE_REFRESHING){
if(newPadding>0){
changeHeaderViewByState(STATE_RELEASE_TO_REFRESH);
}else{
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
}
}
}else{
if(state==STATE_RELEASE_TO_REFRESH||state==STATE_PULL_TO_REFRESH){
intnewPadding=(int)(headerView.getPaddingTop()+vector);
newPadding=Math.max(newPadding,-1*headerHeight);
headerView.setPadding(0,newPadding,0,0);
if(newPadding<=-1*headerHeight){
changeHeaderViewByState(STATE_NON);
headerIsHanding=false;
}elseif(newPadding<=0){
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
}else{

}
}
}
}

(MotionEventev){
footerIsHanding=true;
floattempY=ev.getRawY();
floatvector=tempY-prevY;
vector/=2;
prevY=tempY;
if(vector<0){
intnewPadding=(int)(footerView.getPaddingTop()-vector);
if(newPadding>0){
newPadding=0;
}
footerView.setPadding(0,newPadding,0,0);
if(state!=STATE_REFRESHING){
if(newPadding<0){
changeFooterViewByState(STATE_PULL_TO_REFRESH);
}else{
changeFooterViewByState(STATE_RELEASE_TO_REFRESH);
}
}
}else{
intnewPadding=(int)(footerView.getPaddingTop()-vector);
newPadding=Math.min(newPadding,footerHeight);
footerView.setPadding(0,newPadding,0,0);
if(newPadding<=-1*footerHeight){
changeFooterViewByState(STATE_NON);
footerIsHanding=false;
}elseif(newPadding<0){
changeFooterViewByState(STATE_PULL_TO_REFRESH);
}
}
}

❹ android listview 分頁顯示載入第二頁之後怎麼是從第一條開始顯示的

你要顯示的數據應該是保存在一個List集合里的,只需要把新查詢出的數據加入到原本數據的集合中,在調用adapter.notifyDataSetChange()方法就可以了。
估計你是用第二頁的數據替換帶第一頁的數據了或是把第二頁的數據放到集合前面了。

❺ android中ui怎麼實現選擇題代碼

1、 Android的四大組件是哪些,它們的作用?
答:Activity:Activity是Android程序與用戶交互的窗口,是Android構造塊中最基本的一種,它需要為保持各界面的狀態,做很多持久化的事情,妥善管理生命周期以及一些跳轉邏輯
service:後台服務於Activity,封裝有一個完整的功能邏輯實現,接受上層指令,完成相關的食物,定義好需要接受的Intent提供同步和非同步的介面
Content Provider:是Android提供的第三方應用數據的訪問方案,可以派生Content Provider類,對外提供數據,可以像資料庫一樣進行選擇排序,屏蔽內部數據的存儲細節,向外提供統一的借口模型,大大簡化上層應用,對數據的整合提供了更方便的途徑
BroadCast Receiver:接受一種或者多種Intent作觸發事件,接受相關消息,做一些簡單處理,轉換成一條Notification,統一了Android的事件廣播模型
2、 請介紹下Android中常用的五種布局。
常用五種布局方式,分別是:FrameLayout(框架布局),LinearLayout (線性布局),AbsoluteLayout(絕對布局),RelativeLayout(相對布局),TableLayout(表格布局)。
一、FrameLayout:所有東西依次都放在左上角,會重疊,這個布局比較簡單,也只能放一點比較簡單的東西。二、LinearLayout:線性布局,每一個LinearLayout裡面又可分為垂直布局(android:orientation=」vertical」)和水平布局(android:orientation=」horizontal」 )。當垂直布局時,每一行就只有一個元素,多個元素依次垂直往下;水平布局時,只有一行,每一個元素依次向右排列。三、AbsoluteLayout:絕對布局用X,Y坐標來指定元素的位置,這種布局方式也比較簡單,但是在屏幕旋轉時,往往會出問題,而且多個元素的時候,計算比較麻煩。四、RelativeLayout:相對布局可以理解為某一個元素為參照物,來定位的布局方式。主要屬性有:相對於某一個元素android:layout_below、 android:layout_toLeftOf相對於父元素的地方android:layout_alignParentLeft、android:layout_alignParentRigh;五、TableLayout:表格布局,每一個TableLayout裡面有表格行TableRow,TableRow裡面可以具體定義每一個元素。每一個布局都有自己適合的方式,這五個布局元素可以相互嵌套應用,做出美觀的界面。
3、 android中的動畫有哪幾類,它們的特點和區別是什麼
答:兩種,一種是Tween動畫、還有一種是Frame動畫。Tween動畫,這種實現方式可以使視圖組件移動、放大、縮小以及產生透明度的變化;另一種Frame動畫,傳統的動畫方法,通過順序的播放排列好的圖片來實現,類似電影。
4、 android 中有哪幾種解析xml的類?官方推薦哪種?以及它們的原理和區別。
答:XML解析主要有三種方式,SAX、DOM、PULL。常規在PC上開發我們使用Dom相對輕鬆些,但一些性能敏感的資料庫或手機上還是主要採用SAX方式,SAX讀取是單向的,優點:不佔內存空間、解析屬性方便,但缺點就是對於套嵌多個分支來說處理不是很方便。而DOM方式會把整個XML文件載入到內存中去,這里Android開發網提醒大家該方法在查找方面可以和XPath很好的結合如果數據量不是很大推薦使用,而PULL常常用在J2ME對於節點處理比較好,類似SAX方式,同樣很節省內存,在J2ME中我們經常使用的KXML庫來解析。
5、 ListView的優化方案
答:1、如果自定義適配器,那麼在getView方法中要考慮方法傳進來的參數contentView是否為null,如果為null就創建contentView並返回,如果不為null則直接使用。在這個方法中盡可能少創建view。
2、給contentView設置tag(setTag()),傳入一個viewHolder對象,用於緩存要顯示的數據,可以達到圖像數據非同步載入的效果。
3、如果listview需要顯示的item很多,就要考慮分頁載入。比如一共要顯示100條或者更多的時候,我們可以考慮先載入20條,等用戶拉到列表底部的時候再去載入接下來的20條。
6、 請介紹下Android的數據存儲方式。
答:使用SharedPreferences存儲數據;文件存儲數據;SQLite資料庫存儲數據;使用ContentProvider存儲數據;網路存儲數據;
Preference,File, DataBase這三種方式分別對應的目錄是/data/data/Package Name/Shared_Pref, /data/data/Package Name/files, /data/data/Package Name/database 。
一:使用SharedPreferences存儲數據
首先說明SharedPreferences存儲方式,它是 Android提供的用來存儲一些簡單配置信息的一種機制,例如:登錄用戶的用戶名與密碼。其採用了Map數據結構來存儲數據,以鍵值的方式存儲,可以簡單的讀取與寫入,具體實例如下:
void ReadSharedPreferences(){
String strName,strPassword;
SharedPreferences user = getSharedPreferences(「user_info」,0);
strName = user.getString(「NAME」,」」);
strPassword = user getString(「PASSWORD」,」」);
}
void WriteSharedPreferences(String strName,String strPassword){
SharedPreferences user = getSharedPreferences(「user_info」,0);
uer.edit();
user.putString(「NAME」, strName);
user.putString(「PASSWORD」 ,strPassword);
user.commit();
}

❻ 性能優化實踐(三)-卡頓優化思考

Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,這也意味著程序的大多數操作都必須在16ms內完成。如果無法完成,則發生丟幀,上一幀畫面被重復顯示,造成卡頓的視覺。

從整個視圖渲染流程看:

Surfaceflinger由init啟動的獨立進程,提供合成視圖的系統服務。如果Surfaceflinger掛掉,會重啟zygote。

在Surfaceflinger的init方法中,實例化了HWComposer和兩個EventThread。

HWComposer :負責輸出硬體產生或軟體模擬的Vsync信號。

EventThread :負責分發vsync到Choreographer和SurfaceFlinger。其中mEventThread對應Choreographer;而mSFEventThread:對應SurfaceFlinger。

VSYNC信號主要的兩個訂閱者:SurfaceFlinger 和 Choreographer。

SurfaceFlinger :接收信號執行合成Layer流程。

Choreographer :接收信號來控制同步處理輸入(Input)、動畫(Animation)、繪制(Draw)三個UI操作。

Choreographer通知應用層繪制、SurfaceFlinger負責合成視圖、兩者之前加上了一定的offset,這樣能保證兩者步調一致。

在這個過程中,CPU負責把視圖加工為多邊形和紋理。GPU負責把多邊形和紋理做柵格化處理,成為送顯的像素數據。

1 視圖層面的問題

包括layout層級太深View太多、View太復雜、重復繪制、ListView沒優化、動畫設計不合理等等。

這是遇到卡頓問題首先需要排查的,部分問題可以通過開發階段的coding規范來避免的。

1)layout層級太深View太多:可以通過Lint來檢測,優化:通過合理容器的使用,優先減少層級,其次減少View數目,能重用的盡量重用。

2)View太復雜:如果是自定義View,那還是從視圖太深、View太多兩個層面來考慮優化。如果是成熟的View:比如WebView、VideoView這種重量級的View,盡量復用和管理好生命周期。

3)重復繪制:通過Settings中打開GPU過度繪制 & GPU呈現模式可以了解當前視圖層級關系,當然這部分與前面兩點也是分不開的,最基本的要注意移除xml中非必須背景。

4)ListView優化,這部分主要是convertView的復用,能減少View的創建;ViewHolder的使用,減少View的find和賦值,加快載入速度;分頁載入:控制一次載入的數據量,這樣載入速度會快,內存壓力也相對小。

5)動畫:合理設計動畫,能不用幀動畫盡量不用,因為圖片比較占內存,尤其是數量多的時候。另外針對屬性動畫,同一個view的一系列動畫,可以使用Keyframe+PropertyValuesHolder組合方式達到只使用一個ObjectAnimator,多個view的動畫用AnimatorSet進行動畫組合和排序。

2 消息相關耗時

我們都知道,耗時操作放到子線程做,通過handle返回主線程更新UI。但是消息本身也是會耗時的,主要分兩方面:1)消息本身執行耗時, 2)消息執行被delay。消息本身執行耗時那就是主線程耗時,消息執行被delay,在messageQueue中,由於之前的Message太多或者執行時間過長,導致當前需更新UI的操作得不到及時處理,尤其是16.6ms硬性標准下,一旦delay必然丟幀。

3 主線程耗時

這部分我要說的並不是在主線程做耗時操作了,而是站在CPU調度的角度來看耗時問題,也就是說,比如主線程有500ms的耗時,要麼Running了多久,是否存在Sleeping和Uninterruptible sleep等狀態,這段時間內CPU被搶佔了壓根就沒騰出功夫來執行你這操作。如果有現場的話,通過抓systrace能比較明顯看出來。

4 Input事件本身耗時

在Android整個Input體系中有三個重要的成員:Eventhub,InputReader,InputDispatcher。它們分別擔負著各自不同的職責,Eventhub負責監聽/dev/input產生Input事件,InputReader負責從Eventhub讀取事件,並將讀取的事件發給InputDispatcher,InputDispatcher則根據實際的需要具體分發給當前手機獲得焦點實際的Window,最終交給ActivityThread通過消息來處理。

系統角度:
InputDispatcher分發事件給Window這個過程是跨進程通信,獲取對應window本身可能存在耗時。

應用角度:
客戶端接收事件的消息本身又可能存在耗時和delay的情況,這又回到消息耗時的范疇了。

5 持鎖耗時
這屬於業務邏輯層面的問題,最簡單的就是主線程死鎖,亦或是主線程在等鎖,然後當前鎖被其他線程持有在做耗時操作等等。

6 頻繁GC

我們知道,執行GC操作的時候,所有線程的任何操作都會需要暫停,等待GC操作完成之後,其他操作才能夠繼續運行。通常來說,單個的GC並不會佔用太多時間,但是大量不停的GC操作則會顯著佔用幀間隔時間(16ms)。如果在幀間隔時間裡面做了過多的GC操作,那麼自然其他類似計算,渲染等操作的可用時間就變得少了。

導致GC頻繁執行有兩個原因:

1)內存抖動,在memory monitor里能很明顯看出來,短時間內創建大量對象然後又迅速被釋放。

比如:在一個方法里for循環拼接String。會產生大量廢棄的String對象,短時間內又會被回收,所以容易造成抖動,可以用StringBuilder/StringBuffer來替代,它們實現是動態數組,初始長度128,不夠用了通過array來增加長度。對象統一管理,不會短時間內造成短時間內大量創建和銷毀的問題,同時append與+相比更安全。

2)瞬間產生大量的對象會嚴重佔用Young Generation的內存區域,當達到閥值,剩餘空間不夠的時候,也會觸發GC。即使每次分配的對象佔用了很少的內存,但是他們疊加在一起會增加Heap的壓力,從而觸發更多其他類型的GC。這個操作有可能會影響到幀率,並使得用戶感知到性能問題。

1 內存原因

在系統內存非常低的情況下,常規經驗是:MemAvailable 低於MemTotal 1/10的情況下,容易出現內存引起的卡頓,原因無非就是在內存低的情況下內核在分配內存時,很難從物理內存(夥伴系統)直接拿到合適大小的頁面,此時會觸發回收操作,如內存整理(compact)、回收匿名頁(swap)、回收文件頁(dirty=回寫,clean=丟棄)等操作。這些回收操作較慢,因此耗時。這個過程主要體現在新啟一個應用,zygote fork進程申請內存的時候。

2 系統服務持鎖耗時

應用binder call請求系統服務,一般來說,系統服務如AMS、WMS對應的方法,一上來先不管三七二十一,就是一把大鎖,很多情況下,特定的操作會造成持鎖耗時的情況,具體問題具體分析。

3 CPU調度問題

這類情況不太多見,但是也是存在的。在某個繪制周期中,CPU被搶占,無法及時開始繪制操作。這分幾部分來看,首先是不是被某個進程搶占的,比如dex2oat。或者看這段時間CPU使用率非常高,但是可能是大核跑滿了,但是小核相對比較閑,這屬於系統調度有問題等等。

例如:dex2oat發生的時候,佔用所有有CPU(默認策略是有多少個核,就啟動多少個線程),會將原文件中的dex文件抽出來,逐個指令的判斷,然後進行翻譯,並生成大量的中間內容,這些在memory當中是保存不下的,所以採用了swap機制, memory越少,越容易發生交換,所以還可能引起IO上的瓶頸。

可以設置系統屬性:dalvik.vm.bg-dex2oat-threads 和 dalvik.vm.dex2oat-threads ,這兩個系統屬性是分別設置在前後台執行dex2oat限制的線程數,對應8核CPU來說,比如設置前後台分別為4,這樣dex2oat執行時間會變長,但是卡頓會被緩解。

當然還有一種情況是,當手機溫度過高,導致CPU降頻,也會出現系統卡頓。

本文只是對卡頓分析提供一點不成熟的小思路。隨著學習的深入,我會持續更新。
文中牽涉到的布局和重復繪制相關的內容可以參考我的文章: 布局優化
文中牽扯到的相關性能優化工具可以參考我的系列文章: 性能優化工具篇總結

❼ android listview 分頁載入動態數據:通過json獲取了100條數據,如何將這一百條數據分頁顯示

自定義adapter 定義一個頁碼數,每頁item數量 getcount返回為每頁item數量
getitem返回為 (頁碼數-1)*每頁item數量 +position的那條數據
然後getview方法的數據載入同理 然後給兩個按鈕 當點擊上一頁或者下一頁的時候 就改變頁碼數 然後調用 adapter.notifyDataSetChanged() 大概思路 可能哪裡要稍微改一下

熱點內容
java漢諾塔遞歸演算法 發布:2025-04-02 06:28:40 瀏覽:126
可執行文件是編譯鏈接後生成的文 發布:2025-04-02 04:36:44 瀏覽:174
電腦文件加密軟體免費 發布:2025-04-02 03:02:51 瀏覽:806
php圖片管理 發布:2025-04-02 03:01:11 瀏覽:266
然後弄編程 發布:2025-04-02 02:54:06 瀏覽:114
解壓室俱樂部 發布:2025-04-02 02:47:04 瀏覽:282
安卓哪裡下載文豪野犬 發布:2025-04-02 02:45:04 瀏覽:790
優酷安卓怎麼免廣告 發布:2025-04-02 02:30:07 瀏覽:834
安卓系統怎麼把繁體字改為簡體字 發布:2025-04-02 02:14:39 瀏覽:326
androidpos機 發布:2025-04-02 01:40:54 瀏覽:374