2016/4/29
GitHub:https://github.com/qq137712630/MeiZiNews
碗豆夹下载地址:http://www.wandoujia.com/apps/com.ms.meizinewsapplication
#MeiZiNews
- 性能监听:BlockCanary
- MVP代码框架:MVPro
- 增加Model层:框架模式MVP在Android中的使用
- 日志:logger
- RxJava 使用:给 Android 开发者的 RxJava 详解
- 网络请求:retrofit+okhttp3
- 图片加载:glide
- 搜索控件:SearchView
- 数据库框架:GreenDAO
- retrofit+okhttp3日志显示 okhttp3:logging-interceptor
- 视频播放:PLDroidPlayer
- 一个垂直方向的DrawerLayout,抽屉从上向下展开
- 夜间模式:Colorful 动态换肤开源库
- 修改状态栏:Android 状态栏工具类(实现沉浸式状态栏/变色状态栏):StatusBarUtil
#夜间模式:Colorful
地址:https://github.com/hehonghui/Colorful/issues/5
最后我在 clearRecyclerViewRecyclerBin下, 增加((RecyclerView) rootView).getRecycledViewPool().clear();`解决了
public class RecyclerViewSetter extends ViewGroupSetter {
public RecyclerViewSetter(ViewGroup targetView, int resId) {
super(targetView, resId);
}
public RecyclerViewSetter(ViewGroup targetView) {
super(targetView);
}
@Override
protected void clearRecyclerViewRecyclerBin(View rootView) {
super.clearRecyclerViewRecyclerBin(rootView);
((RecyclerView) rootView).getRecycledViewPool().clear();
}
@Override
public void setValue(Resources.Theme newTheme, int themeId) {
clearRecyclerViewRecyclerBin(mView);
// 遍历子元素与要修改的属性,如果相同那么则修改子View的属性
for (ViewSetter setter : mItemViewSetters) {
for (int i = 0; i < ((RecyclerView) mView).getAdapter().getItemCount(); i++) {
View itemView = ((RecyclerView) mView).getChildAt(i);
if (itemView == null) {
continue;
}
boolean isBaseAdapterHelper = ((RecyclerView) mView).getChildViewHolder(itemView) instanceof BaseAdapterHelper;
if (!isBaseAdapterHelper) {
continue;
}
BaseAdapterHelper baseAdapterHelper = (BaseAdapterHelper) ((RecyclerView) mView).getChildViewHolder(itemView);
setter.mView = baseAdapterHelper.getView(setter.mViewId);
int itemId = setter.getViewId();
if (baseAdapterHelper.getView(itemId) == null) {
continue;
}
setter.setValue(newTheme, themeId);
}
}
}
}
#RxJava
按照以下顺序阅读的话,大概就理解如何使用了
RxJava 使用:给 Android 开发者的 RxJava 详解
图**有 5 处含有对事件的操作。
由图中可以看出,①和②两处受第一个 subscribeOn() 影响,运行在红色线程;
③和④处受第一个 observeOn() 的影响,运行在绿色线程;
⑤处受第二个 observeOn() 影响,运行在紫色线程;
而第二个 subscribeOn() ,由于在通知过程中线程就被第一个 subscribeOn() 截断,因此对整个流程并没有任何影响。
这里也就回答了前面的问题:当使用了多个 subscribeOn() 的时候,只有第一个 subscribeOn() 起作用。
subscribeOn()
设置影响在它以上的操作,只有第一个 ````subscribeOn`````起效;
observeOn()
设置影响在它以下的操作,直到下一个 observeOn()
设置。
#GreenDAO
- GreenDAO数据库版本升级
- 使用greenDAO遇到的问题:Cannot update entity without key - was it inserted before?
- greenDao数据库更新和多表关联
- 多个有关联的表查询:joins
- 查找查询中的问题,将sql语句打印出来:GreenDao简明教程(查询,Querying)
DbUtil
/**
* Built SQL for query:
* SELECT T."_id",T."HTML_ID",T."COLLECT" FROM "COLLECT_ENTITY" T
* JOIN HTML_ENTITY J1 ON T."HTML_ID"=J1."_id"
* WHERE J1."URL"=?
*
* @param url
* @return
*/
public List<CollectEntity> queryCollectByhtmlUrl(String url) {
QueryBuilder<CollectEntity> queryBuilder = collectEntityDao.queryBuilder();
queryBuilder.join(CollectEntityDao.Properties.Html_id, HtmlEntity.class, HtmlEntityDao.Properties.Id)
.where(HtmlEntityDao.Properties.Url.eq(url));
return queryBuilder.list();
}
#Retrofit 使用
##转换器
retrofit RxAndroid+Retrofit环境搭建
转换器可以被添加到支持其他类型。六兄弟模块适应流行的序列化库为您提供方便。 Converters can be added to support other types. Six sibling modules adapt popular serialization libraries for your convenience.
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
直接返回String类型需引入:ScalarsConverterFactory.create()
retrofit = new Retrofit.Builder()
.client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//设置不同的底层网络库
.baseUrl(strBaseUrl)
.addConverterFactory(ScalarsConverterFactory.create())//添加 String类型[ Scalars (primitives, boxed, and String)] 转换器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器
.build();
返回Gson类型需引入:GsonConverterFactory.create()
retrofit = new Retrofit.Builder()
.client(MyOkHttpClient.getMyOkHttpClient().getOkHttpClient())//设置不同的底层网络库
.baseUrl(strBaseUrl)
.addConverterFactory(GsonConverterFactory.create())//添加 json 转换器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器
.build();
##Retrofit 结合 RxJava使用
-
添加依赖
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4' compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4' compile 'io.reactivex:rxandroid:1.1.0' compile 'io.reactivex:rxjava:1.1.0'
前两个是 Retrofit 和 Gson 的依赖,第三个是 Retrofit 中 RxJava 转换器的依赖,最后两个就是 RxJava 和 Rx Android 的依赖
-
编写 API
使用 RxJava 的情况下,接口文件稍有修改,接口中的方法的返回类型不再是 Call 而是 Observable 类型:
public interface GitHub { @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo); //使用 RxJava 的方法,返回一个 Observable @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo); }
结合 RxJava 使用的 接口就定义好了,模型类不需要变动,接下来直接进行网络请求
-
网络请求
使用 RxJava 的 Retrofit 可以直接在 主线程中编写。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Retrofit retrofit=new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create())//添加 json 转换器 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器 .build(); GitHub gitHub=retrofit.create(GitHub.class); gitHub.RxContributors("square","retrofit") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<Contributor>>() { @Override public void onCompleted() { Log.i("TAG","onCompleted"); } @Override public void onError(Throwable e) { } @Override public void onNext(List<Contributor> contributors) { for (Contributor c:contributors){ Log.i("TAG","RxJava-->"+c.getLogin()+" "+c.getId()+" "+c.getContributions()); } } }); }
##使用Retrofit和Okhttp实现网络缓存。无网读缓存,有网根据过期时间重新请求
okhttp 请求设置官方文档 使用Retrofit和Okhttp实现网络缓存。无网读缓存,有网根据过期时间重新请求 OuNews 新闻:RetrofitManager类下的 initOkHttpClient 方法 okhttp3.X,retrofit:2.0.0-beta4适用
-
配置okhttp中的Cache
OkHttpClient okHttpClient; File cacheFile = new File(DemoActivity.this.getCacheDir(), "[缓存目录]"); Cache cache = new Cache(cacheFile, 1024 * 1024 * 10); //100Mb okHttpClient = new OkHttpClient.Builder() .cache(cache) .build();
栗子:
// 完成缓存 public class MyOkHttpClient { //设缓存有效期为两天 protected static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2; //查询缓存的Cache-Control设置,为if-only-cache时只查询缓存而不会请求服务器,max-stale可以配合设置缓存失效时间 protected static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC; //查询网络的Cache-Control设置,头部Cache-Control设为max-age=0时则不会使用缓存而请求服务器 protected static final String CACHE_CONTROL_NETWORK = "max-age=0"; private String TAG = "MyOkHttpClient"; private OkHttpClient okHttpClient; private static MyOkHttpClient myOkHttpClient; public static MyOkHttpClient getMyOkHttpClient() { if (myOkHttpClient == null) { myOkHttpClient = new MyOkHttpClient(); } return myOkHttpClient; } /** * 初始化 * * @param mContext */ public void init(final Context mContext) { if (okHttpClient != null) { return; } Log.e(TAG, "初始化okHttpClient"); // 因为BaseUrl不同所以这里Retrofit不为静态,但是OkHttpClient配置是一样的,静态创建一次即可 File cacheFile = new File( mContext.getCacheDir(), "HttpCache" ); // 指定缓存路径 Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); // 指定缓存大小100Mb // 云端响应头拦截器,用来配置缓存策略 Interceptor rewriteCacheControlInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!NetUtil.isConnected(mContext)) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE).build(); Log.e(TAG, "no network"); } Response originalResponse = chain.proceed(request); if (NetUtil.isConnected(mContext)) { //有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置 String cacheControl = request.cacheControl().toString(); return originalResponse.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma").build(); } else { return originalResponse.newBuilder().header("Cache-Control", "public, only-if-cached," + CACHE_STALE_SEC) .removeHeader("Pragma").build(); } } }; okHttpClient = new OkHttpClient.Builder().cache(cache) .addNetworkInterceptor(rewriteCacheControlInterceptor) .addInterceptor(rewriteCacheControlInterceptor) .connectTimeout(30, TimeUnit.SECONDS).build(); } public OkHttpClient getOkHttpClient() { return okHttpClient; } /** * 根据网络状况获取缓存的策略 * * @return */ @NonNull public static String getCacheControl(Context mContext) { return NetUtil.isConnected(mContext) ? CACHE_CONTROL_NETWORK : CACHE_CONTROL_CACHE; } }
-
配置请求头中的cache-control
//使用 RxJava 的方法,返回一个 Observable @Headers("Cache-Control: public, max-age=3600") @GET("/repos/{owner}/{repo}/contributors") Observable<List<Contributor>> RxContributors(@Path("owner") String owner,@Path("repo") String repo);
-
配置请求 Retrofit 设置不同的底层网络库
Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create())//添加 json 转换器 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加 RxJava 适配器 .build();
-
开启打印连接日志
/** * 开启打印连接日志 * @return */ private HttpLoggingInterceptor getHttpLoggingInterceptor() { boolean isDebug= false;//是否开启 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); if(isDebug) { interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); } //开启打印连接日志 return interceptor; } OkHttpClient.Builder().addInterceptor(getHttpLoggingInterceptor()) okHttpClient = new OkHttpClient.Builder() .addInterceptor(getHttpLoggingInterceptor()) .cache(cache) .addNetworkInterceptor(rewriteCacheControlInterceptor) .addInterceptor(rewriteCacheControlInterceptor) .connectTimeout(30, TimeUnit.SECONDS).build();
参考 ````` MzituImgListModel ````
/**
* Retrofit 使用Observable.from发出多条连接
* @param context
* @param listener
* @return
*/
@Override
public Subscription loadWeb(final Context context, final OnModelListener<ImgItem> listener) {
MyStringRetrofit.getMyStringRetrofit().init(context, MeiZiApi.MZITU_API);
final MzituApi mzituApi = MyStringRetrofit.getMyStringRetrofit().getCreate(MzituApi.class);
Observable observable = mzituApi.RxImgList(
MyOkHttpClient.getCacheControl(context),
imgId,
page
).flatMap(
new Func1<String, Observable<Integer>>() {
@Override
public Observable<Integer> call(String s) {
// 第一次请求先请求页数,从而获得要发的链接数
Elements mElements = JsoupUtil.getMzituImgPage(s);
Elements tempElements = mElements.select("span");
String size = tempElements.get(tempElements.size() - 2).text();
if (TextUtils.isEmpty(size)) {
return null;
}
ArrayList<Integer> numList = new ArrayList<Integer>();
for (int i = 1; i <= Integer.parseInt(size); i++) {
numList.add(i);
}
return Observable.from(numList);
}
}
).flatMap(
new Func1<Integer, Observable<String>>() {
@Override
public Observable<String> call(Integer integer) {
// 发出单次网络请求连接
return mzituApi.RxImgList(
MyOkHttpClient.getCacheControl(context),
imgId,
integer + ""
);
}
}
).map(
new Func1<String, ImgItem>() {
@Override
public ImgItem call(String s) {
// 处理每次请问的结果
ImgItem img = new ImgItem();
Elements mElements = JsoupUtil.getMzituImgItem(s);
Element tempElement = mElements.first();
img.setImgUrl(tempElement.select("img").attr("src"));
img.setUrl(tempElement.select("img").attr("src"));
img.setStory_title(tempElement.select("img").attr("alt"));
return img;
}
}
);
return RxJavaUtil.rxIoAndMain(
observable,
new Subscriber<ImgItem>() {
@Override
public void onCompleted() {
listener.onCompleted();
}
@Override
public void onError(Throwable e) {
listener.onError(e.toString());
DebugUtil.debugLogErr(e, e.toString());
}
@Override
public void onNext(ImgItem imgItem) {
listener.onSuccess(imgItem);
}
}
);
}
@Path:路径
@Query:查询条件,如:
xx=yy
如果有多个查询条件可以使用: @QueryMap
;
#API
##视频直播
通过webview打开HTML5页面用自己的浏览器点直播MP4和M3U8 【M3U8】测试地址及android播放器
###斗鱼
分类
http://www.douyu.com/directory
进入分类
http://www.douyu.com/directory/game/DOTA2?page=2&isAjax=1
HTML5版-房间信息
http://m.douyu.com/html5/live?roomId=73334
{
"error": 0,
"msg": "ok",
"data": {
"room_id": "73334",
"tag_name": "DOTA2",
"room_src": "http://rpic.douyucdn.cn/z1605/01/15/73334_160501151547.jpg",
"room_name": "重返8000之路!7600",
"show_status": "1",
"online": 13126,
"nickname": "两仪落214",
"hls_url": "http://hls3a.douyutv.com/live/73334rRfZajWPCGs_550/playlist.m3u8?wsSecret=c52bca967d3b341d878291b7901c800b&wsTime=1462084010",
"is_pass_player": false,
"is_ticket": 0
}
}
分类:
http://www.panda.tv/cate
分页
http://www.panda.tv/ajax_sort?pageno=1&pagenum=20&classification=lol
http://www.panda.tv/ajax_sort?token=&pageno=2&pagenum=120&classification=lol
房间:
http://www.panda.tv/354095
房间信息
http://room.api.m.panda.tv/index.php?method=room.shareapi&roomid=354095
http://room.api.m.panda.tv/index.php?method=room.shareapi&roomid=354095&callback=jQuery1910274208655487177_1462807801662&_=1462807801663
http://room.api.m.panda.tv/index.php?method=room.recommend&callback=jQuery1910274208655487177_1462807801664&_=1462807801665
// 专题
http://www.mzitu.com/zhuanti
// 首页
http://www.mzitu.com/
http://www.mzitu.com/page/1
// 热门
http://www.mzitu.com/hot
http://www.mzitu.com/hot/page/2
// 推荐
http://www.mzitu.com/best
http://www.mzitu.com/best/page/2
// 性感妹子
http://www.mzitu.com/xinggan
http://www.mzitu.com/xinggan/page/2
// 日本妹子
http://www.mzitu.com/japan
http://www.mzitu.com/japan/page/2
// **妹子
http://www.mzitu.com/taiwan
http://www.mzitu.com/taiwan/page/2
// 清纯
http://www.mzitu.com/mm
http://www.mzitu.com/mm/page/2
// 图集查看
http://www.mzitu.com/{图集ID}/{页数}
http://www.mzitu.com/65054/48
永久免费的基于深度学习的中文在线抽词-PullWord API 使用
API:
http://103.37.149.178:16888/pullword/get.php?source=清华大学是好学校¶m1=0¶m2=1
//Zhihu API
public static final String BASE_URL = "http://news-at.zhihu.com/api/4/news/";//日报详情
public static final String NEWS_LATEST = "http://news-at.zhihu.com/api/4/news/latest";//最新日报
public static final String NEWS_BEFORE = "http://news-at.zhihu.com/api/4/news/before/";//指定日期的日报
public static final String SPLASH = "http://news-at.zhihu.com/api/4/start-image/1080*1920";//封面
http://news-at.zhihu.com/api/4/themes // 主题日报列表查看
http://news-at.zhihu.com/api/4/theme/{id} //主题日报内容查看
-
日报详情
BASE_URL+日报ID http://news-at.zhihu.com/api/4/news/7942211
-
最新日报
直接访问:NEWS_LATEST http://news-at.zhihu.com/api/4/news/latest
-
指定日期的日报
NEWS_BEFORE+(指定日期+1天),格式为全数字,YYYYMMDD,如: 2016年02月29号的数据: http://news-at.zhihu.com/api/4/news/before/20160301
-
主题日报
http://news-at.zhihu.com/api/4/themes // 主题日报列表查看 http://news-at.zhihu.com/api/4/theme/{id} //主题日报内容查看
public static String DB_BREAST = "http://www.dbmeinv.com/dbgroup/show.htm?cid=2&pager_offset=";
public static String DB_BUTT = "http://www.dbmeinv.com/dbgroup/show.htm?cid=6&pager_offset=";
public static String DB_SILK = "http://www.dbmeinv.com/dbgroup/show.htm?cid=7&pager_offset=";
public static String DB_LEG = "http://www.dbmeinv.com/dbgroup/show.htm?cid=3&pager_offset=";
public static String DB_RANK="http://www.dbmeinv.com/dbgroup/rank.htm?pager_offset=";
网址
例子:
http://androidweekly.cn/page/2/
#MD实践
- Android Design Support Library使用详解
- NestedScrollView: 兼容MD的 ScrollView
- Android L动画入门
- ANDROID L——Material Design详解(动画篇)
- Android M新控件之FloatingActionButton,TextInputLayout,Snackbar,TabLayout的使用
- Android M新控件之AppBarLayout,NavigationView,CoordinatorLayout,CollapsingToolbarLayout的使用
##侧滑 NavigationView
最快的方法是创一下新的侧滑模块
- windowDrawsSystemBarBackgrounds:如何将DrawerLayout显示在ActionBar/Toolbar和status bar之间
- Android 自己实现 NavigationView [Design Support Library(1)]
- android, NavigationView's setItemTextColor and setItemIconTinList have no effects
代码中:
int[][] states = new int[][] {
new int[] { android.R.attr.state_selected }, // selected
new int[] { android.R.attr.state_focused, android.R.attr.state_pressed }, // pressed
new int[] { }, // default
};
int[] colors = new int[] {
0,
0,
getColor(newTheme)
};
navigationView.setItemTextColor(new ColorStateList(
states,
colors
));
Xml
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
android:background="?attr/root_view_bg"
app:headerLayout="@layout/nav_header_main"
app:itemTextColor="?attr/text_color"
app:menu="@menu/activity_main_drawer" />
##上下拉:SwipeRefreshLayout
- Android SwipeRefreshLayout 官方下拉刷新控件介绍
- RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout
- 实例解析之SwipeRefreshLayout+RecyclerView+CardView
- Google官方下拉刷新组件--SwipeRefreshLayout
-
该使用 fitsSystemWindows 了!或该使用 fitsSystemWindows 了!
以哪个为固定: android:fitsSystemWindows="true"
总结: 为了使得Toolbar有滑动效果,必须做到如下三点:
注:CoordinatorLayout如果是最外的布局要去掉:
1、CoordinatorLayout必须作为整个布局的父布局容器。 2、 给需要滑动的组件设置 app:layout_scrollFlags=”scroll|enterAlways” 属性。 3、给你的可滑动的组件,也就是RecyclerView 或者 NestedScrollView 设置如下属性:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
RecyclerView使用介绍 base-adapter-helper的RecyclerView版
SwipeRefreshLayout.OnRefreshListener
下拉监听
RecyclerView.OnScrollListener
到底监听
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
onListScrolled();
}
}
});
其中 getItemViewType 用来返回当前项是哪种类型布局, getViewTypeCount 返回当前ListView总共多少种类型的布局
如果在 RecyclerView 实现多种Item,只需要实现一个 getItemType 方法,用来返回item的种类
在 onCreateViewHolder 和 onBindViewHolder 方法中,第二个参数就是item的类型
-
为实现Fragment 不会重新创,仿OuNews的 BaseFragment类中的onCreateView和onDestroyView写法
-
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int lastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); int totalItemCount = mLayoutManager.getItemCount(); //lastVisibleItem >= totalItemCount - 4 表示剩下4个item自动加载,各位自由选择 // dy>0 表示向下滑动 if (lastVisibleItem >= totalItemCount - 4 && dy > 0) { if(isLoadingMore){ Log.d(TAG,"ignore manually update!"); } else{ loadPage();//这里多线程也要手动控制isLoadingMore isLoadingMore = false; } } } });
- XML布局是否可以像Jsp一样动态加载?
通过清理项目的缓存可解决。
第1个:
04-19 20:57:09.844 31614-31614/com.ms.meizinewsapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ms.meizinewsapplication, PID: 31614
java.lang.NoSuchMethodError: No virtual method setMenuItemIconByCollect(Z)V in class Lcom/ms/meizinewsapplication/features/main/iview/DevWeekDetailIVew; or its super classes (declaration of 'com.ms.meizinewsapplication.features.main.iview.DevWeekDetailIVew' appears in /data/data/com.ms.meizinewsapplication/files/instant-run/dex/slice-slice_4-classes.dex)
at com.ms.meizinewsapplication.features.main.activity.DevWeekDetailActivity$2.onMenuItemClick(DevWeekDetailActivity.java:160)
at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:169)
at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:760)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948)
at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:618)
at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:139)
at android.view.View.performClick(View.java:4908)
at android.view.View$PerformClick.run(View.java:20378)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5691)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
第2个:
04-19 20:13:37.194 5338-5416/com.ms.meizinewsapplication E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-2
Process: com.ms.meizinewsapplication, PID: 5338
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NoSuchMethodError: No virtual method addCollectByUrl(Ljava/lang/String;Ljava/lang/String;)Z in class Lcom/ms/meizinewsapplication/features/base/utils/db/DbUtil; or its super classes (declaration of 'com.ms.meizinewsapplication.features.base.utils.db.DbUtil' appears in /data/data/com.ms.meizinewsapplication/files/instant-run/dex/slice-slice_9-classes.dex)
at com.ms.meizinewsapplication.features.base.model.CollectModel$1.call(CollectModel.java:44)
at com.ms.meizinewsapplication.features.base.model.CollectModel$1.call(CollectModel.java:39)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:46)
at rx.internal.util.ScalarSynchronousObservable$1.call(ScalarSynchronousObservable.java:35)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable.unsafeSubscribe(Observable.java:8098)
at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
app首次打开要很长时间 First launch take long time in Android Studio 2.0 & Gradle 2.0
所报日志:
04-27 10:24:16.241 12442-12450/com.ms.meizinewsapplication W/art: Suspending all threads took: 8.750ms
04-27 10:24:16.394 12442-12442/com.ms.meizinewsapplication W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
04-27 10:24:16.552 12442-12802/com.ms.meizinewsapplication D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
[ 04-27 10:24:16.554 12442:12442 D/ ]
HostConnection::get() New Host Connection established 0xb4305d40, tid 12442
04-27 10:24:16.556 12442-12442/com.ms.meizinewsapplication D/Atlas: Validating map...
原因:
在2.0版本的新功能已添加即时运行。
要启用此功能的工具添加了大量的元信息,所以第一个构建和上传需要更多的时间。
请注意有关设置的minSdkVersion 15或更高,以获得任何盈利。
In version 2.0 a new feature was added instant-run.
To enable this feature tool adds a lots of meta information, so the first build and upload takes more time.
Be aware about setting minSdkVersion 15 or higher to get any profit.
解决步骤:
1、点击:Settings → Build, Execution, Deployment → Instant Run
2、取消选择:Enable Instant Run to hot swap code/resoure changes on deploy
这样会取消功能即时运行
,但可以解决这个问题。
主要还是BaseQuickAdapter为样本,重写getItemViewType
,主要修改如下:
/**
* 返回的布局判断
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if (position == 0 || oldCount == position) {
return TYPE_TITLE;
} else {
return TYPE_ITEM;
}
}
/**
* 对不同类型的操作
* @param viewGroup
* @param viewType
* @return
*/
@Override
public BaseAdapterHelper onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = null;
switch (viewType) {
case TYPE_TITLE:
view = LayoutInflater.from(viewGroup.getContext()).inflate(titleLayoutResId, viewGroup, false);
break;
case TYPE_ITEM:
view = LayoutInflater.from(viewGroup.getContext()).inflate(itemLayoutResId, viewGroup, false);
break;
}
if (view == null) {
return null;
}
view.setOnClickListener(this);
BaseAdapterHelper vh = new BaseAdapterHelper(view);
return vh;
}