android开发之ListView

##ListView功能很强大,类似Windows下的列表菜单。
如下实现一个简单的ListView.

<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>

###通过内置的适配器进行界面关联

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

private String[] data = {"apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}

其中ArrayAdapter的构造函数参数为:

  1. 上线文
  2. 子项布局ID
  3. 数据

加入图片

新建布局fruit_item.xml如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>

用类来封装子项数据

class Fruit
{
private String name;
private int imageId;
public Fruit(String name,int imageId)
{
this.imageId = imageId;
this.name = name;
}
public String getName()
{
return name;
}
public int getImageId()
{
return imageId;
}
}

重新实现一个FruitAdapter

class FruitAdapter extends ArrayAdapter<Fruit>
{
private int resourceId;

public FruitAdapter(Context context,int textViewResourceId, List<Fruit> objects)
{
super(context, textViewResourceId ,objects );
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position); //获取当前想的Fruit实例
View view; // convertView 已加载view进行缓存
ViewHolder viewHolder;
if (convertView == null)
{
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView)view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
}
else
{
view = convertView;
viewHolder = (ViewHolder)view.getTag(); //重新得到ViewHolder
}

viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder
{
ImageView fruitImage;
TextView fruitName;
}
}

注意上面代码对ListView有两处优化,分别为:

  1. convertView 为上次缓存View,若不为空即可不用重新生成View
  2. 用ViewHolder将已经findViewById成功的控件保存起来,防止每次都在布局中找而耗时

最终入口代码如下:

class FruitAdapter extends ArrayAdapter<Fruit>
{
private int resourceId;

public FruitAdapter(Context context,int textViewResourceId, List<Fruit> objects)
{
super(context, textViewResourceId ,objects );
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position); //获取当前想的Fruit实例
View view; // convertView 已加载view进行缓存
ViewHolder viewHolder;
if (convertView == null)
{
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView)view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
}
else
{
view = convertView;
viewHolder = (ViewHolder)view.getTag(); //重新得到ViewHolder
}

viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder
{
ImageView fruitImage;
TextView fruitName;
}
}
public class MainActivity extends AppCompatActivity {

//private String[] data = {"apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
private List<Fruit> fruitList = new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
*/
InitFruits();
FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
}
private void InitFruits()
{
for (int i = 0 ; i < 2 ; ++i)
{
Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
fruitList.add(mango);
}
}
}

android开发之activity

  1. Activity:界面交互
  2. Intent: 数据传递

###一个主界面

<activity android:name=".MyActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

其中"android.intent.action.MAIN"表示主界面,apk启动时大多从这里开始启动.
"android.intent.category.LAUNCHER"展示在系统的LAUNCHER中,没有这个就没有启动图标。

###创建一个菜单
首先创建一个菜单的布局文件

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id="@+id/remove_item"
android:title="Remove"/>
<item
android:id="@+id/exit_item"
android:title="Exit"/>

</menu>

Activity中实现

//设置菜单界面    
//返回值true表示显示
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}

//相应菜单事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.add_item:
Toast.makeText(MyActivity.this,"add_item",Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(MyActivity.this,"remove_item",Toast.LENGTH_SHORT).show();
break;
case R.id.exit_item:
finish();
break;
default:
break;
}
return true;
}

###界面间交互
用Intent进行参数传递
首先是 AndroidManifest.xml文件中如下声明

<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.cn.yllen.myapplication.ACTION_START" />
<category android:name="com.cn.yllen.myapplication.MY_CATECORY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

####分别有显式隐式两种intent方式启动activity

button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(MyActivity.this,"Hi~~",Toast.LENGTH_SHORT).show();
//显示 intent
//Intent intent = new Intent(MyActivity.this,SecondActivity.class);
//隐式 intent
Intent intent = new Intent("com.cn.yllen.myapplication.ACTION_START");
intent.addCategory("com.cn.yllen.myapplication.MY_CATECORY");
startActivity(intent);
}
});

####打开网页


Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://ring3.xyz"));
Toast.makeText(SecondActivity.this,Uri.parse("http://ring3.xyz").toString(),Toast.LENGTH_SHORT).show();
startActivity(intent);

####传递参数 & 接收参数

String data = "你好世界";
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);
/*-----------*---------------*-----------*/
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d(TAG, "onCreate: " + data);

####启动activity并获取返回值

// 通过startActivityForResult方法启动activity并获得返回数据
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
startActivityForResult(intent,1);

//同时需要重写 onActivityResult 方法,以便获取返回值
@Override
protected
void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (requestCode)
{
case 1:
if (resultCode == RESULT_OK)
{
String returnedData = data.getStringExtra("ret_data");
Log.d(TAG, "onActivityResult: " + returnedData);
}
break;
default:
break;
}
}

/*----------------*------------*---------------*/
// 被启动的activity,通过 setResult方法 返回数据
Button button = (Button) findViewById(R.id.button_3);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("ret_data","这是返回值哦~");
setResult(RESULT_OK,intent);
finish();
}
});

activity生存期


启动模式

声明方式

<activity
...
android:launchMode="xx">
...
</activity>

standard:每次都生成一个新的activity放在栈顶

startActivity(xx.this,xx.class);
startActivity(xx.this,xx.class);
startActivity(xx.this,xx.class);
//则需要按三次back键进行返回

singleTop:仅当不在栈顶时生成

//当前界面可视
startActivity(xx.this,xx.class);
startActivity(xx.this,xx.class);
startActivity(xx.this,xx.class);
//则需要按一次back键进行返回

singleTask:全栈唯一

singleInstance:多栈

自定布局

首先自己完成一个布局类的子类

package com.cn.yllen.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

/**
* Created by Administrator on 2017/2/27 0027.
*/
public class TitleLayout extends LinearLayout {
public TitleLayout(final Context context , AttributeSet attrs)
{
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.layout_title,this);
Button button1 = (Button) findViewById(R.id.title_back);
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"back",Toast.LENGTH_SHORT).show();
}
});
}
}

对应的xml布局文件layout_title.xml实现如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_bg">

<Button
android:id="@+id/title_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="3dp"
android:background="@drawable/back_bg"
android:text="Back"
android:textColor="#fff"
/>
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:gravity="center"
android:text="Title text"
android:textColor="#fff"
android:textSize="24sp"
android:background="@drawable/title_bg"
/>

<Button
android:id="@+id/button_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="3dp"
android:background="@drawable/edit_bg"
android:textColor="#fff"
android:text="确定3"
/>
</LinearLayout>

将上面布局文件引入其他布局中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.cn.yllen.myapplication.TitleLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.cn.yllen.myapplication.TitleLayout>
</LinearLayout>

效果图如下:

android开发之内容提供者

一个获取手机联系人小Demo
布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="com.cn.yllen.contentprovider.MainActivity">
<ListView
android:id="@+id/conent_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>

加入权限申明

<uses-permission android:name="android.permission.READ_CONTACTS" ></uses-permission>

实现代码如下

import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity {

List<String> contactsList = new ArrayList<>();
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView view = (ListView) findViewById(R.id.conent_view);
adapter = new ArrayAdapter(this,android.R.layout.simple_expandable_list_item_1,contactsList);
view.setAdapter(adapter);
readContacts();
}

private void readContacts()
{
Cursor cursor = null;
try
{
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if (cursor != null)
{
while(cursor.moveToNext())
{
//联系人
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
//手机号
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName + "\n" + number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e)
{
e.printStackTrace();
}finally {
if (cursor != null)
cursor.close();
}
}
}

android开发之简单http请求

直接代码了
通过

private void sendRequestWithHttpURLConnection()
{
//开启线程发起网络请求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL("http://ring3.xyz");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
InputStream in = connection.getInputStream();

//读取输入流
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while((line = reader.readLine()) != null)
{
response.append(line);
}
showResponse(response.toString());
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (reader != null) {
try{
reader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}

同时记录下安卓下异步事件分发,如下场景
在子线程中进行UI的操作,比如更新某个文本,由于android的UI交互是线程不安全的,所以界面操作只能在主线程中进行,那么,就要想办法让子线程通知主线程了,可以通过Handler实现
在子线程中构造一个Message,然后通过Handler对象进行通知

new Thread(new Runnable())
{
@Override
public void run()
{
...
Message msg = new Message();
msg.what = x;
handler.sendMessage(msg); //通知
...
}
}
主线程
private Handler handler = new Handler()
{
public void handleMessage(Message msg)
{
xxx
}
}

android中异步消息处理主要由四部分构成
Message 需要传递的信息
Handler 最终处理者
Looper 每个线程一个,负责取消息与分发
MessageQueue 每个线程一个,存放消息队列

smali汇编学习

# HelloWorld.smali
.class public LHelloWorld; # 定义类名
.super Ljava/lang/Object; # 父类
.method public static main([Ljava/lang/String;)V #声明静态main()方法
.locals 3 #程序使用v0、v1、v2 以及一个参数
#.prologue #代码起始指令
#数据定义指令
const/16 v0, 0x8
const/4 v1, 0x5
const/4 v2, 0x3
#数据操作指令
move v1, v2
#数组操作指令
new-array v0,v0,[I
array-length v1,v0 #获取数组长度
#实例操作指令
new-instance v1,Ljava/lang/StringBuilder;
#方法调用
invoke-direct {v1},Ljava/lang/StringBuilder;-><init>()V
#跳转指令
if-nez v0,:cond_0
goto :goto_0
:cond_0
#数据转换
int-to-float v2,v2
#数据运算指令
add-float v2,v2,v2
#比较指令
cmpl-float v0,v2,v2
#字段操作指令
sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1 , "Hi,MobSec" #构造字符串
#方法调用
invoke-virtual {v0,v1},Ljava/io/PrintStream;->println(Ljava/lang/String;)V
#返回指令
:goto_0
return-void
.end method

使用smali.jar将HelloWorld.smali编译为DEX文件
命令如下

java -jar smali.jar assemble -o HelloWorld.dex HelloWorld.smali

接着,需要将这个smali压缩为zip格式的压缩包HelloWorld.zip
然后将这个压缩包拷贝到手机中,或者是模拟器中
adb命令如下

adb push HelloWorld.zip /data/local
[100%] /data/local/HelloWorld.zip
E:\mobileSec\tools\build>adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld
Hi,MobSec

Dex格式学习

1.dex header
制定dex文件的一些属性,记录其他6部分数据结构在dex文件中的物理偏移

2.string_ids、type_ids、type_ids、proto_ids、field_ids、method_ids、class_def
索引结构区

3.data
真实数据存储在这里

未经过优化的dex文件结构如下:

struct DexFile
{
DexHeader Header;
DexStringId StringIds[stringIdsSize];
DexTypeId TypeIds[typeIdsSize];
DexProtoId ProtoIds[protoIdsSize];
DexFieldId FieldIds[fieldIdsSize];
DexMethodId MethodIds[methodIdsSize];
DexClassDef ClassDefs[classDefsSize];
DexData Data[];
DexLink LinkData;
}

dalvik虚拟机在解析Dex文件的时候,首先根据Dex Header中DexMapList字段,将内容映射到内存