Android开发中RecycleView + CardView 控件
先上效果图:
原理图:
这是RecycleView的工作原理:
1.LayoutManager用来处理RecycleView的“列表”样式,Support包默认包含了:LinearLayoutManager 横向或纵向的滚动列表、
GridLayoutManager 网格列表、StaggeredGridLayoutManager 交错的网格列表。
2.Adapter负责处理RecycleView的数据和样式
3.在传统的ListView中有一种常见的写法是使用ViewHolder来缓存数据集,在新版的RecycleView内置了ViewHolder这一模块,所以在Adapter内部新建内部类ViewHolder。
4.RecycleView 和listView的一个区别就是本身不处理点击事件,点击事件应该绑在ViewHolder里面,可以直接写也可以通过接口绑在Adapter里面来实现。
首先添加数据集:
package com.lfk.drawapictiure.Info; /** * Created by liufengkai on 15/9/13. */ public class MenuInfo { private String paint_name; private String paint_time; private String paint_root; private String paint_img_root; public MenuInfo(String paint_name, String paint_time, String paint_root, String paint_img_root) { this.paint_name = paint_name; this.paint_time = paint_time; this.paint_root = paint_root; this.paint_img_root = paint_img_root; } public String getPaint_name() { return paint_name; } public String getPaint_time() { return paint_time; } public String getPaint_root() { return paint_root; } public String getPaint_img_root() { return paint_img_root; } }
实现继承自RecycleView的Adapter中间要包裹自己实现的ViewHolder,onCreateviewHolder函数和onBindViewHolder实现了ListView里面getView的工作,分别为找到控件和控件赋值,
实现点击的接口,设置接口并且绑在ViewHolder的itemView里面即根视图中。
public class MainLayoutAdapter extends RecyclerView.Adapter<MainLayoutAdapter.MainViewHolder> { private LayoutInflater inflater; private ArrayList<MenuInfo> userList; private Context context; private MainItemClickListener itemClickListener; public MainLayoutAdapter(ArrayList<MenuInfo> userList, Context context) { this.userList = userList; this.context = context; this.inflater = LayoutInflater.from(context); } public void setItemClickListener(MainItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } //onCreateviewHolder函数和onBindViewHolder实现了ListView里面getView的工作,分别为找到控件和控件赋值 @Override public MainLayoutAdapter.MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View wrapper = inflater.inflate(R.layout.draw_item, parent, false); return new MainViewHolder( wrapper, (TextView)wrapper.findViewById(R.id.paint_name), (TextView)wrapper.findViewById(R.id.paint_time), (TextView)wrapper.findViewById(R.id.paint_root), (ImageView)wrapper.findViewById(R.id.paint_img)); } @Override public void onBindViewHolder(MainViewHolder holder, int position) { MenuInfo menuInfo = userList.get(position); holder.paint_img.setImageURI(Uri.parse(menuInfo.getPaint_img_root())); holder.paint_name.setText(menuInfo.getPaint_name()); holder.paint_time.setText(menuInfo.getPaint_time()); holder.paint_root.setText(menuInfo.getPaint_root()); } @Override public int getItemCount() { return userList.size(); } public class MainViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{ private TextView paint_name; private TextView paint_time; private TextView paint_root; private ImageView paint_img; public MainViewHolder(View itemView, TextView paint_name, TextView paint_time, TextView paint_root, ImageView paint_img) { super(itemView); itemView.setOnClickListener(this); this.paint_name = paint_name; this.paint_time = paint_time; this.paint_root = paint_root; this.paint_img = paint_img; } @Override public void onClick(View view) { MenuInfo menuInfo = userList.get(getAdapterPosition()); itemClickListener.onItemClick(view,menuInfo.getPaint_name(),menuInfo.getPaint_root()); } @Override public boolean onLongClick(View view) { return false; } } }
Activity中的使用:
package com.lfk.drawapictiure.Fragment; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.lfk.drawapictiure.Adapter.MainLayoutAdapter; import com.lfk.drawapictiure.Info.MenuInfo; import com.lfk.drawapictiure.InterFace.MainItemClickListener; import com.lfk.drawapictiure.MainActivity; import com.lfk.drawapictiure.R; import java.util.ArrayList; public class PaintFragment extends android.support.v4.app.Fragment { private RecyclerView mRecyclerView; private MainLayoutAdapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private String path = Environment.getExternalStorageDirectory().getPath() + "/DrawAPicture"; public static PaintFragment newInstance() { return new PaintFragment(); } public PaintFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View wrapper = inflater.inflate(R.layout.fragment_paint, container, false); mRecyclerView = (RecyclerView)wrapper.findViewById(R.id.paint_recycle_view); mLayoutManager = new LinearLayoutManager(getActivity()); mRecyclerView.setLayoutManager(mLayoutManager); //绑上列表管理器 ArrayList<MenuInfo> arrayList = new ArrayList<>(); arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg")); arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg")); arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg")); arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg")); mAdapter = new MainLayoutAdapter(arrayList, getActivity()); // 设置点击事件 mAdapter.setItemClickListener(new MainItemClickListener() { @Override public void onItemClick(View view, String name, String path) { Intent intent = new Intent(getActivity(), MainActivity.class); intent.setData(Uri.parse(path)); startActivity(intent); } }); //绑定数据集 mRecyclerView.setAdapter(mAdapter); return wrapper; } }
其中的子布局使用了CardView:
我设置的东西只有:
card_view:cardCornerRadius="4dp" //设定圆角半径 card_view:cardElevation="8dp"//设定阴影 <?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:background="@drawable/list_item_selector" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp" card_view: android:layout_margin="@dimen/card_margin"> <RelativeLayout android:layout_marginTop="16dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:maxHeight="100dp" android:layout_marginTop="8dp" android:layout_below="@+id/line_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/paint_img" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/paint_name" android:textColor="#000" android:textSize="18sp" android:text="项目名" android:layout_alignParentRight="true" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/paint_time" android:text="项目时间" android:textColor="@color/black" android:textSize="18sp" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <View android:id="@+id/line_view" android:background="@color/gray" android:layout_below="@+id/paint_time" android:layout_width="match_parent" android:layout_height="1dp"/> <TextView android:id="@+id/paint_root" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> </android.support.v7.widget.CardView>
Android L——RecyclerView,CardView导入和使用
这篇文章是ANDROID L——Material Design详解(UI控件)的一个补充或者说是应用实例,如果有时间建议大家稍微浏览一下上篇文章。
本文主要介绍Android L新增加的两个UI控件RecyclerView,CardView的导入和使用。
RecyclerView是ListView的升级版
CardView则是Google提供的一个卡片式视图组件
本例就是使用RecyclerView来展示多个CardView的一个小例子,先看下效果图:
导入RecyclerView,CardView
由于RecyclerView,CardView是放在support library v7包中,所以我们想要使用就必须要导包。
下面就介绍下在Eclipse和Android Studio中是如何导入这两个包的。
Eclipse:
第一步:通过SDK manager下载/更新Android Support Libraries(5.0版本最新为21)
第二步:导入CardView和RecyclerView项目(都在support v7中)
1.在Eclipse中点击Import,导入Android项目
2.导入CardView和RecycleView,路径为your sdk path\extras\android\support\v7\cardview(RecycleView则为相同目录下的recyclerview)
3.导入时记得将工程copy到本地并建议重命名,这样方便以后管理例如:
第三步:设置Library
1..将两个工程设置为Library
2..在主工程中引入这两个Library例如:
通过这三步就可以将这两个包导入进来了。
Android Studio
Android Stuido相对于Eclipse简单的多:
第一步:
首先要确保已经将Android Support Libraries升级到最新.
第二步:
打开项目中的build.gradle文件,在dependencies中添加如下代码。
dependencies {
compile 'com.android.support:recyclerview-v7:21.+'
compile 'com.android.support:cardview-v7:21.+'
}
第三步:
重新Build一下工程。
Build完成后就会发现这两个包就已经导入进来了
代码介绍:
主题:
首先这个黑色基调的主题是使用了Material.Dark.ActionBar样式。
设置方法:修改values-v21文件夹下styles.xml文件:
<resources> <style name="AppTheme" parent="android:ThemeOverlay.Material.Dark.ActionBar"> </style> </resources>
布局文件:
recycler_view.xml(RecyclerView布局文件):
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MyActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MyActivity" /> </FrameLayout>
FrameLayout里包含了RecyclerView控件
card_view.xml(CardView布局文件):
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="5dp" android:orientation="horizontal" card_view:cardBackgroundColor="@color/cardview_dark_background" card_view:cardCornerRadius="5dp" > <RelativeLayout android:layout_width="match_parent" android:layout_height="100dp" android:padding="5dp" > <ImageView android:id="@+id/pic" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:scaleType="centerCrop" /> <TextView android:clickable="true" android:id="@+id/name" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="10dp" android:layout_marginRight="10dp" android:gravity="right|bottom" android:textColor="@android:color/white" android:textSize="24sp" /> </RelativeLayout> </android.support.v7.widget.CardView>
CardView视图中包含了一个ImageView和一个TextView分别显示图片和文字信息
唯一需要介绍的就是在布局文件中使用了,如下两个属性:
card_view:cardBackgroundColor="@color/cardview_dark_background"
card_view:cardCornerRadius="5dp"
他俩的作用分别是设置CardView的背景颜色和外围的圆角大小(注意要使用card_view命名空间)
代码:
Actor类(封装数据的Model类):
public class Actor { String name; String picName; public Actor(String name, String picName) { this.name = name; this.picName = picName; } public int getImageResourceId( Context context ) { try { return context.getResources().getIdentifier(this.picName, "drawable", context.getPackageName()); } catch (Exception e) { e.printStackTrace(); return -1; } } }
封装了演员的名字和图片名,getImageResourceId()方法的作用就是根据图片命找到系统资源
MyActivity(程序主控制Activity)
public class MyActivity extends Activity { private RecyclerView mRecyclerView; private MyAdapter myAdapter; private List<Actor> actors = new ArrayList<Actor>(); private String[] names = { "朱茵", "张柏芝", "张敏", "巩俐", "黄圣依", "赵薇", "莫文蔚", "如花" }; private String[] pics = { "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8" }; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate(savedInstanceState); setContentView(R.layout.recycler_view); actors.add(new Actor("朱茵", "p1")); getActionBar().setTitle("那些年我们追的星女郎"); // 拿到RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.list); // 设置LinearLayoutManager mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); // 设置ItemAnimator mRecyclerView.setItemAnimator(new DefaultItemAnimator()); // 设置固定大小 mRecyclerView.setHasFixedSize(true); // 初始化自定义的适配器 myAdapter = new MyAdapter(this, actors); // 为mRecyclerView设置适配器 mRecyclerView.setAdapter(myAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { // 当点击actionbar上的添加按钮时,向adapter中添加一个新数据并通知刷新 case R.id.action_add: if (myAdapter.getItemCount() != names.length) { actors.add(new Actor(names[myAdapter.getItemCount()], pics[myAdapter.getItemCount()])); mRecyclerView.scrollToPosition(myAdapter.getItemCount() - 1); myAdapter.notifyDataSetChanged(); } return true; // 当点击actionbar上的删除按钮时,向adapter中移除最后一个数据并通知刷新 case R.id.action_remove: if (myAdapter.getItemCount() != 0) { actors.remove(myAdapter.getItemCount()-1); mRecyclerView.scrollToPosition(myAdapter.getItemCount() - 1); myAdapter.notifyDataSetChanged(); } return true; } return super.onOptionsItemSelected(item); } }
MyAdapter(自定义适配器类)
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<Actor> actors; private Context mContext; public MyAdapter( Context context , List<Actor> actors) { this.mContext = context; this.actors = actors; } @Override public ViewHolder onCreateViewHolder( ViewGroup viewGroup, int i ) { // 给ViewHolder设置布局文件 View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_view, viewGroup, false); return new ViewHolder(v); } @Override public void onBindViewHolder( ViewHolder viewHolder, int i ) { // 给ViewHolder设置元素 Actor p = actors.get(i); viewHolder.mTextView.setText(p.name); viewHolder.mImageView.setImageDrawable(mContext.getDrawable(p.getImageResourceId(mContext))); } @Override public int getItemCount() { // 返回数据总数 return actors == null ? 0 : actors.size(); } // 重写的自定义ViewHolder public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ImageView mImageView; public ViewHolder( View v ) { super(v); mTextView = (TextView) v.findViewById(R.id.name); mImageView = (ImageView) v.findViewById(R.id.pic); } } }
所有代码介绍完毕了,可以总结为以下两点:
RecyclerView:
理解为之前的ListView,不过需要设置LinearLayoutManager(目前资料不多我也有点迷糊以后再补充)和ItemAnimator(为每个条目设置操作动画)两个新属性
RecyclerView.Adapter:
理解为默认自带和基于ViewHolder的新的适配器,只不过回调方法稍有不同,但本质都是一样的。
为了解决android手机适配问题,我们经常想如果可以按照百分比的方式进行界面布局,这样适配各种屏幕就简单多了吧!现在谷歌正式提供百分比布局支持库(android-support-percent-lib)。
这个库提供了:
两种布局供大家使用: PercentRelativeLayout、PercentFrameLayout,通过名字就可以看出,这是继承自FrameLayout和RelativeLayout两个容器类;
支持的属性有:
layout_widthPercent、layout_heightPercent、
layout_marginPercent、layout_marginLeftPercent、
layout_marginTopPercent、layout_marginRightPercent、
layout_marginBottomPercent、layout_marginStartPercent、layout_marginEndPercent。
可以看到支持宽高,以及margin。
也就是说,大家只要在开发过程中使用PercentRelativeLayout、PercentFrameLayout替换FrameLayout、RelativeLayout即可。
使用:
关于使用,其实及其简单,并且github上也有例子,android-percent-support-lib-sample。
使用Android studio在build.gradle添加以下信息就可以获取支持库,当然了,如果你没有下载到该支持库会提示你下载。
{5330C3E9-D43A-40E0-A247-83C217BE9A84}
PercentRelativeLayout使用小例子:
布局文件如下:
<android.support.percent.PercentRelativeLayout xmlns:android=" http://schemas.android.com/apk/res/android " xmlns:app=" http://schemas.android.com/apk/res-auto " android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/row_one_item_one" android:layout_width="0dp" android:layout_height="0dp" android:layout_alignParentTop="true" android:background="#7700ff00" android:text="w:70%,h:20%" android:gravity="center" app:layout_heightPercent="20%" app:layout_widthPercent="70%"/> <TextView android:id="@+id/row_one_item_two" android:layout_width="0dp" android:layout_height="0dp" android:layout_toRightOf="@+id/row_one_item_one" android:background="#396190" android:text="w:30%,h:20%" app:layout_heightPercent="20%" android:gravity="center" app:layout_widthPercent="30%"/> <ImageView android:id="@+id/row_two_item_one" android:layout_width="match_parent" android:layout_height="0dp" android:scaleType="centerCrop" android:layout_below="@+id/row_one_item_one" android:background="#d89695" app:layout_heightPercent="70%"/> <TextView android:layout_width="0dp" android:layout_height="0dp" android:layout_below="@id/row_two_item_one" android:background="#770000ff" android:gravity="center" android:text="width:100%,height:10%" app:layout_heightPercent="10%" app:layout_widthPercent="100%"/> </android.support.percent.PercentRelativeLayout
效果图如下
因为没有LinearLayout我们可以自己来自定义。刚好在网上看到有自定义好的PercentLinearlayout,分享给大家
public class PercentLinearlayout extends LinearLayout{ private PercentLayoutHelper mPercentLayoutHelper; public PercentLinearlayout(Context context, AttributeSet attrs) { super(context, attrs); mPercentLayoutHelper = new PercentLayoutHelper(this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mPercentLayoutHelper.handleMeasuredStateTooSmall()) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mPercentLayoutHelper.restoreOriginalParams(); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } public static class LayoutParams extends LinearLayout.LayoutParams implements PercentLayoutHelper.PercentLayoutParams { private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); } @Override public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() { return mPercentLayoutInfo; } @Override protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } } }
使用效果如下:
下文是一个Android自定义Spinner的例子,我们下面就一起来看看这篇文章希望能够让各位深入的理解Spinner的用法,具体例子如下所示。
最近在做的项目中有很多下拉框,为了实现方便就用了Android 自带的Spinner,但是自带的Spinner的样式又不符合要求,就学习了一下自定义Spinner。下面是整个步骤:
1.准备好图片
2.style中定义
<!-- spinner --> <style name="spinner_style"> <item name="android:background">@drawable/spinner</item> <item name="android:paddingLeft">5dip</item>
3.调用
<Spinner android:id="@+id/field_item_spinner_content" style="@style/spinner_style" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
4.在layout中定义simple_spinner_item.xml
<?xml version="1.0" encoding="utf-8"?> <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" android:paddingLeft="5dip" android:paddingRight="5dip" android:gravity="center_vertical" android:textColor="#808080" android:singleLine="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
5.java代码
ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,R.layout.simple_spinner_item); String level[] = getResources().getStringArray(R.array.affair_level);//资源文件 for (int i = 0; i < level.length; i++) { adapter.add(level[i]); } adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter);
效果图:
android studio 在安卓虚拟机运行的过程出现中文乱码问题肯定是我们配置有问题了,我们下面来为各位介绍这个问题的解决 办法,具体如下。
一、前言
对抗反编译是指让apk文件或者dex文件无法正常通过反编译工具,而且有可能导致工具异常或者崩溃,如apktool、baksmali、dex2jar、JEB等等工具,如下图dex2jar无法正常工作。
二、Dex文件格式解析
目前大多数android软件的反编译工具都是开源的,比如apktool、Dex2jar、baksamli,大家可以非常方便的从github下载并源阅读代码,然后找到可以利用的点,再在自己的软件中加入干扰代码,让反编译工具出现异常或者无法正常阅读代码。
接下来让我们先来熟悉一下dex文件格式 ,一个dex文件由以下几个部份组成:
1. Dex Header: Dex结构头它指定了dex文件的一些基本属性,并记录了部份数据表在dex文件中的物理偏移。 2. String Table: 字符串表,存储字符串的索引和个数 3. Type Table: 类型表,存储类型的索引和个数 4. Proto Table: 函数元型表,存储函数元型索引和个数 5. Field Table: 字段表,存储字段索引和个数 6. Method Table: 方法表,存储方法索引和个数 7. Class def Table:类定义表,存储类定义索引和个数 8. Data Section: 存储数据,由以上类型的索引查找,
有兴趣的可以直接翻看android的源码或者参考以下链接:
http://www.netmite.com/android/mydroid/dalvik/docs/dex-format.html
既然要在代码中添加干扰指令,接下的DexClassDef结构,肯定是要了解的非常清楚。
结构中包含了类的类型偏移、访问标志、父类类型索引、接口偏移、注释、静态类型的偏移信息,整体结构图定义如下:
struct DexClassDef { u4 classIdx; u4 accessFlags; u4 superclassIdx; u4 interfacesOff; u4 sourcefileIdx; u4 annotationsOff; u4 classDataOff; u4 staticValuesOff; }
classIdx 字段是一个索引值,类的类型,做为下标索引在DexTypeID结构列表中查找
accessFlags 字段是类的访问标志,以ACC_开头的枚举值
superclassIdx 字段是父类的类型,做为下标索引在DexTypeID结构列表中查找
interfacesOff 字段是接口类型,做为下标索引在DexTypeList结构列表中查找
sourcefileIdx 字段是源文件名,做为下标索引在DexTypeList结构列表中查找
annotationsOff 字段是注释信息的偏移,指向DexAnnotationsDirectoryItem结构
classdataOff 字段是指向了DexClassData结构体的偏移
staticValuesOff 字段是指向DexEncodeArray结构体的偏移,记录静态数据的信息
DexClassData结构说明:
struct DexClassData { DexClassDataHeader header; DexField* staticFields; //静态字段,DexField结构 DexField* instanceFields; //实例字段,DexField结构 DexMethod* directMethods; //直接方法,DexMethod结构 DexMethod* virtualMethods; //虚方法, DexMethod结构 }
DexClassData结构中的DexMethod类型描述了:方法的原型、名称、访问标志以及代码数据块,codeOff字段指向了一个DexCode结构,它描述了方法更详细的信息以及方法中指令的内容。
DexMethod结构声明如下
struct DexMethod { u4 methodIdx; //指向DexMethodId的索引 u4 accessFlags;//访问标志 u4 codeOff; //指向dexCode的结构偏移 } struct DexCode { ushort registerSsize;//使用的寄存器的数目 ushort insSize; //传入参数的数目 unshort outsSize; //调用其他方法时使用的寄存器个数 unshort triesSize; //try/catch异常块个数 uint debugInfoOff; //调试信息的偏移 uint insnsSize; //指令集个数 ushort insns[1]; //指令集数组,变长数组 }
DexClassData树型结构图:
:
三、调试dex2jar工程
1. 将dex2jar源码导入IntelliJ IDEA,导入后IntelliJ IDEA会自动查找对应 Grable并下载,需要比较长的时间等待
源码地址: https://github.com/pxb1988/dex2jar
2. 选中Grable中的dex2jar/dex2jar/Tasks/other/antlr2java进行编译
3. 点击工具栏的 Project Structure, 然后选择Modules -> d2j-smali -> build,然后点击Excluded
4. 选择 build/generated-sources/antlr, 然后点击 Sources
5. 最后打开Excluded Gradle Task弹出对话框,运行clean distZip 命令
执行完Gradl clean distZip命令后会在\dex2jar-2.x\dex-tools\build\distributions目录下生成 dex-tools-2.1-SNAPSHOT.zip,压缩包内是编译完后生成的jar文件和一些配置信息文件。大家请参考dex2jar.sh文件,它向我们说明需要使用lib目录下的所有jar文件和入口函数com.googlecode.dex2jar.tools.Dex2jarCmd 才能将dex文件转换成jar文件
6.将dex文件转换成jar文件需要执行转换命令
格式如下:
java -Xms512m -Xmx1024m -classpath .\lib\*.jar “com.googlecode.dex2jar.tools.Dex2jarCmd” classdex.dex
根据以上的命令,来配置调试参数,设置如下:
7.设置完成后,就可以开始调试
四、dex2jar反编译失败原因分析
1.首先我们从解包失败的错误异常入手,定位崩溃处的代码。
通过错误提示可以定位到dex2jar崩溃处的代码,源码位置如下:
dex-ir\src\main\java\com\googlecode\dex2jar\ir\TypeClass.java
2. 在崩溃的函数处下断点,开始调试。
a)通过抛出的异常的说明“cant not merge I and Z”,以及整个调用堆栈。
我们可以看出造成这个异常的原因是函数调用时,实参的类型和形参类型不匹配引起的。因为在Java语法中,将实参是布尔类型传递给形参是int类型。这样是不合法的,所以导致dex2jar在检查的参数类型的时候失败。
b)知道了崩溃的原因,那我们就需要确定它具体是使用怎样的方法做到的,通过dex2jar生成的error异常信息详细说明文件得知该dex文件中的每一个函数头部都加了一句这样的代码Exit.b(Exit.a())请看Ida,现在大致能猜到混淆者所做的工作:
1.首先解析dex文件格式,定位到DexCode结构中的insns成员
2.向代码中添加一个类成员对象名字叫Exit,然后添加代码Exit.b(Exit.a())
3.明白了怎么添加干扰代码,接下来分析一下dex2jar的工作流程,dex2jar转换成jar文件这个过程中会验证,函数调用时的形参和实参的合法性,请看下图,解析到INVOKE_开头的指令时,会开始解析返回值,供给参数,和实参等信息,具体逻辑和代码大家有兴趣可以详细地研究一下dex2jar源码。
4.merge负责的工作是参数类型的验证,如果两个类型相同,返回形参的类型,形参为UNKONW返回实参,实参为UNKONW返回形参,两个类型都不相同,则匹配异常
5.最后在dex2jar里添加代码,使dex文件能正确的被反编译成功。这里给出一段可以成功让dex2jar反编译的出jar文件的代码(其实方法有很多)
下图是成功反编译的jar文件
相关文章
- 下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
- 这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 这篇文章主要介绍了C#实现跨线程操作控件方法,主要采用异步访问方式实现,需要的朋友可以参考下...2020-06-25
- 如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
- 深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
- TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
- 这篇文章主要介绍了vscode搭建STM32开发环境的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-05-02
android.os.BinderProxy cannot be cast to com解决办法
本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20- 这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
- 下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
- 首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
- 下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
- 下面我们一起来看一篇关于 安卓开发之Intent传递Object与List的例子,希望这个例子能够为各位同学带来帮助。 Intent 不仅可以传单个的值,也可以传对象与数据集合...2016-09-20
用Intel HAXM给Android模拟器Emulator加速
Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20