android杂记

本文介绍了Android的基础知识,包括屏幕尺寸、像素、分辨率、dpi的概念,以及sp和dp的区别。讲解了Android项目的远程库依赖、jcenter仓库和.so与.jar文件的作用。讨论了Android中的ABI、动态库与静态库的区别,以及Activity的生命周期、屏幕旋转、权限管理和App间数据共享。还提到了RecyclerView的更新方法和View的绘制过程,最后讨论了Bitmap的压缩、内存管理和 EventBus 使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

平时我们所讲的

手机屏幕大小是手机的对角线长度,一般单位为英寸,1英寸为2.54厘米。

像素是想象把屏幕放大出现的一个个小圆点或小方块

分辨率是指屏幕上垂直方向和水平方向上的像素个数 :比如iPhone5S的分辨率是1136*640

dpi指的是每英寸的像素数,也叫做屏幕密度,这个值越大,屏幕越清晰。

使用sp作为字体大小单位,会随着系统的字体大小改变

而dp作为单位则不会.

 

 

Android的项目依赖有一种远程库依赖
比如app需要依赖下面这个远程库,
dependencies {
compile 'com.etsy.android.grid:library:1.0.5'
}
这样定义了, 去哪里拿到库工程的代码和资源文件呢,肯定是要从某个源去获取。
 

jcenter是一个声明仓库的源,之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。

so库

.so文件是Linux系统的可执行文件,相当于windows上的exe执行文件,只可以在Linux系统运行。 so文件就是常说的动态链接库,都是C或C++编译出来的。 与Java比较就是:它通常是用的Class文件(字节码) Linux下的.so文件时不能直接运行的。一般来讲,.so文件称为共享库。 

 

  • so机制让开发者最大化利用已有的C和C++代码,达到重用的效果,利用软件世界积累了几十年的优秀代码;
  • so是二进制,没有解释编译的开消,用so实现的功能比纯java实现的功能要快;
  • so内存分配不受Dalivik/ART的单个应用限制,减少OOM;
  • 相对于java代码,二进制代码的反编译难度更大,一些核心代码可以考虑放在so中。

jar文件

 

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。 
jar包 里面是别人写好的java代码, 里面的类可能实现了你想要的功能, 这样用别人的jar包就不用自己再写一次相同功能的代码了.

总结

jar相当于静态库,so相当于动态库文件。

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

区别

 

ABI

每一种CPU架构,都定义了一种ABI(Application Binary Interface,应用二进制接口),ABI定义了其所对应的CPU架构能够执行的二进制文件(如.so文件)的格式规范,决定了二进制文件如何与系统进行交互。每一个Android应用所支持的ABI是由其APK提供的.so文件决定的,这些so文件被打包在apk文件的lib/目录下

 

android:allowBackup

Android API Level 8及其以上Android系统提供了为应用程序数据的备份和恢复功能,此功能的开关决定于该应用程序中AndroidManifest.xml文件中的allowBackup属性值[1] ,其属性值默认是true。当allowBackup标志为true时,用户即可通过adb backup和adb restore来进行对应用数据的备份和恢复,这可能会带来一定的安全风险。

 

由于Application类是在APP启动的时候就启动,启动在所有Activity之前,所以可以使用它做资源的初始化操作,如图片资源初始化,WebView的预加载,推送服务的注册等等,注意不要执行耗时操作,会拖慢APP启动速度。

 

在非UI线程中其实可以更新UI,前提是要有自己的ViewRoot,而ViewRoot是在onResume()方法里的addView()创建的,所以在onResume()中判断是否为UI线程,在onCreate()中可以通过子线程来刷新UI的(试验:如果子线程sleep(2000),就会报在非UI线程中刷新UI的错误了 )。

 

Android App之间共享SharedPreference

在AndroidManifest.xml中的manifest标签,我们需要设置两个APP的sharedUserId,如下:

<manifest xmlns:
 android="http://schemas.android.com/apk/res/android"
 package="com.example.xiechen.sourceapp" 
android:sharedUserId="xxcc.com">

Activity生命周期:

1、当第一次调用一个Activity就会执行onCreate方法

2、当Activity处于可见状态的时候就会调用onStart方法

3、当Activity可以得到用户焦点的时候就会调用onResume方法

4、当Activity被遮挡住的时候就会调用onPause方法

5、当Activity处于不可见状态的时候就会调用onStop方法

6、当Activity没有被销毁的时候重新调用这个Activity就会调用onRestart方法

7、当Activity被销毁时会调用onDestory方法

onSaveInstanceState方法在onPause方法之后执行在onStop方法之前执行。

屏幕旋转的生命周期:

在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState()一定会被执行,且也一定会执行onRestoreInstanceState()。

    在AndroidManifest.xml设置

  1. android:screenOrientation="landscape"横屏设置;

  2. android:screenOrientation="portrait"竖屏设置;

Activity启动方式:

(在AndroidManifest.xml中修改launchMode属性)

standard(默认):每一次都会创造一个新的Activity实例到返回栈中

singleTop:如果发现返回栈的栈顶已经是该活动,不会再创建新的活动实例。

singleTask:如果返回栈中存在该实例,可以直接使用,并把这个活动之上的所有活动统统出栈。

singleInstance:为该实例创建一个单独的返回栈来管理该实例

Activity与Fragment之间传值:

1.在Activity中创建一个Bundle,将要传的值存入,然后通过fragment的setArguments(bundle)传到fragment,在fragment中用getArguments方法接收。

例:Activity中:

Bundle bundle=new Bundle()

bundle.putString("dad","kkk");//kkk就是要传的值

fragment.setArguments(bundle);

Fragment中:

Bundle bundle=getArguments();

String s=bundle.getString("dad");

2.在Activity中定义一个public的方法,将要传递的值以return返回,在fragment的onAttach()中调用这个方法得到值

例:Activity代码:

public String rr(){

    return "lll";
}

Fragment代码:

@Override

public void onAttach(Context context) {
    super.onAttach(context);
    Log.i("fragment_111111111","onAttach");
    s=((MainActivity)context).rr();//s为全局变量
}

ANP :    Application Not Responsing

不同组件发生ANP的时间不一样,主线程Activity/Service是5秒,broadcast是10秒

解决方案:将所有耗时操作(访问网络,Socket通信,查询大量SQL语句,复杂逻辑计算等)都放到子线程中去,然后通过handler.sendMessage, runonUIThread,  AsyncTask等方式更新UI,无论如何要保证用户界面操作的流畅度,如果耗时操作需要让用户等待,那么可以在界面上显示进度条。

RecyclerView:

 

  • notifyItemChanged(int position) 更新列表position位置上的数据可以调用
  • notifyItemInserted(int position) 列表position位置添加一条数据时可以调用,伴有动画效果
  • notifyItemRemoved(int position) 列表position位置移除一条数据时调用,伴有动画效果
  • notifyItemMoved(int fromPosition, int toPosition) 列表fromPosition位置的数据移到toPosition位置时调用,伴有动画效果
  • notifyItemRangeChanged(int positionStart, int itemCount) 列表从positionStart位置到itemCount数量的列表项进行数据刷新
  • notifyItemRangeInserted(int positionStart, int itemCount) 列表从positionStart位置到itemCount数量的列表项批量添加数据时调用,伴有动画效果
  • notifyItemRangeRemoved(int positionStart, int itemCount) 列表从positionStart位置到itemCount数量的列表项批量删除数据时调用,伴有动画效果

 

View绘制:

 

1.MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得出一个针对子View的测量要求,这个测量要求就是MeasureSpec。

一个MeasureSpec是一个大小跟模式的组合值,MeasureSpec中的值是一个整型(32位)将size和mode打包成一个Int型,其中高两位是mode,后面30位存的是size,是为了减少对象的分配开支。MeasureSpec 类似于下图,只不过这边用的是十进制的数,而MeasureSpec 是二进制存储的。

UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大。
EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。-1

AT_MOST:子容器可以是声明大小内的任意大小。 -2

 

view.measure(int widthMeasureSpec, int heightMeasureSpec) 的两个MeasureSpec是父类传递过来的,但并不是完全是父View的要求,而是父View的MeasureSpec和子View自己的LayoutParams共同决定的,而子View的LayoutParams其实就是我们在xml写的时候设置的layout_width和layout_height 转化而来的。

父View的measure的过程会先测量子View,等子View测量结果出来后,再来测量自己。

 

2.View的测量过程主要是在onMeasure()方法

3.View的Root是DecorView

每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口,每个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,对于Activity来说,ViewRootImpl是连接WindowManager和DecorView的纽带,绘制的入口是由ViewRootImpl的performTraversals方法来发起Measure,Layout,Draw等流程的。

DecorView 测量ViewRoot 的时候把自己的widthMeasureSpec和heightMeasureSpec传进去了,接下来你就要去看measureChildWithMargins的源码了

ViewRoot 是系统的View,它的LayoutParams默认都是match_parent

 

Bitmap.compress方法确实可以压缩图片,但压缩的是存储大小,即你放到disk上的大小

我尝试过把品质设置为10,decode出来的Bitmap大小没变,但显示照片的质量非常差

BitmapFactory.decodeByteArray方法对压缩后的byte[]解码后,得到的Bitmap大小依然和未压缩过一样

如果你想要显示的Bitmap占用的内存少一点,还是需要去设置加载的像素长度和宽度(变成缩略图)

 

OOM(内存泄漏):

原因:

1.资源对象没关闭造成内存泄漏

1-1,Cursor :调用SQLiteDatabase的query()会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。调用它的moveToFirst()方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。通过cursor的getColumnIndex()方法获取到某一列在表中对应的位置索引。例:String name=cursor.getString(cursor.getColumnIndex("name"));


1-2,调用registerReceiver后未调用unregisterReceiver()

  广播:例:定义内部类继承BroadcastReceiver,重写onReceiver()方法,在Activity类中onCreate()方法中调用registerReceiver(内部类实例,IntentFilter),在onDestory()方法中调用unregisterReceiver()


1-3,未关闭InputStream/OutputStream 


1-4,Bitmap使用后未调用recycle()

 

EventBus的使用与BroadcastReceiver、EventBus优缺点分析:

广播是重量级的,消耗资源较多的方式。他的优势体现在与sdk连接紧密,如果需要同 android 交互的时候,广播的便捷性会抵消掉它过多的资源消耗,但是如果不同android交互,或者说,只做很少的交互,使用广播是一种浪费;

在与Android系统交互,比如,网络的变化、电量的变化,短信发送和接收的状态使用Broadcast较为适宜

EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。

EventBus使用:

添加依赖:compile 'org.greenrobot:eventbus:3.0.0'

1.新建一个消息的类或者也可不写,用String对象等

 

  1. public class FirstEvent {  
  2.     private String mMsg;  
  3.     public FirstEvent(String msg) {  
  4.         // TODO Auto-generated constructor stub  
  5.         mMsg = msg;  
  6.     }  
  7.     public String getMsg(){  
  8.         return mMsg;  
  9.     }  
  10. }  

 

2.在要接收消息的页面注册EventBus:比如

在Activity的onCreate()方法中

EventBus.getDefault().register(this),

在onDestroy()方法中

EventBus.getDefault().unregister(this); 

3.接收消息

 

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(FirstEvent s) {
        //类名+方法/事件名
     
    }

 

4.发送消息

 

  1. EventBus.getDefault().post(new FirstEvent("FirstEvent btn clicked"));  


RecyclerView使用initList()和initData()顺序使用很重要,initList()先布局,那么开始适配器中是没有数据的


 

Long l=Long.parseLong(String.valueOf(data.get(position).get("createTime")));
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
intent.putExtra("time",simpleDateFormat.format(date));

 

addHeadView():待完善

 

有左滑删除效果的reyclerView

//左滑删除
compile 'com.yanzhenjie:recyclerview-swipe:1.0.2'

 

Intent传递List<Map<String,Object>>

 

intent.putExtra("maplist", (Serializable) mapList);
dataList = (ArrayList<HashMap<String, Object>>) getIntent().getSerializableExtra("maplist");

 

非常好用的提示窗口 PromapDialog

 

compile 'com.github.limxing:Android-PromptDialog:1.1.3'
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        new PromptDialog(SearchActivity.this).showError("请输入搜索关键词");
    }
}, 100);

 

 

 

getColumnIndexOrThrow如果没有找到该列名,会抛出IllegalArgumentException异常

 

倒计时:

 

cdt=new CountDownTimer(Integer.parseInt(itemlist.get(0).get("secs").toString())*1000,1000) {
    @Override
    public void onTick(long millisUntilFinished) {
        long day=millisUntilFinished/(24*60*60*1000);
        long hour=(millisUntilFinished-day*24*60*60*1000)/(60*60*1000);
        long minute=(millisUntilFinished-day*24*60*60*1000-hour*60*60*1000)/(60*1000);
        long second=(millisUntilFinished-day*24*60*60*1000-hour*60*60*1000-minute*60*1000)/1000;

        if (second>=60){
            second=second % 60;
            minute+=second/60;
        }
        if (minute>=60){
            minute=minute%60;
            hour+=minute/60;
        }
        if (hour>=24){
            hour=hour%24;
            day+=hour/24;
        }
        String Sday="";
        String Shour="";
        String Sminute="";
        String Ssecond="";

        if (day<10){
            Sday="0"+String.valueOf(day);
        }else {
            Sday=String.valueOf(day);
        }

        if (hour<10){
            Shour="0"+String.valueOf(hour);
        }else {
            Shour=String.valueOf(hour);
        }

        if (minute<10){
            Sminute="0"+String.valueOf(minute);
        }else {
            Sminute=String.valueOf(minute);
        }

        if (second<10){
            Ssecond="0"+String.valueOf(second);
        }else {
            Ssecond=String.valueOf(second);
        }

        tv_count_day.setText(Sday);
        tv_count_hour.setText(Shour);
        tv_count_minute.setText(Sminute);
        tv_count_second.setText(Ssecond);
       // tv_count_day.setText(millisUntilFinished);
    }

    @Override
    public void onFinish() {

    }
}.start();
时间戳转换
Long longtime=Long.parseLong(String.valueOf(data.get(position).get("createTime")));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date date;
try {
    date=sdf.parse(sdf.format(longtime));
    tv_time.setText(sdf.format(date));
} catch (ParseException e) {
    e.printStackTrace();
}
//排版良好的网页浏览器
compile 'com.just.agentweb:agentweb:2.0.1'

 
mAgentWeb = AgentWeb.with(this)//传入Activity or Fragment
        .setAgentWebParent(ll_full, new LinearLayout.LayoutParams(-1, -1))//传入AgentWeb 的父控件 ,如果父控件为 RelativeLayout , 那么第二参数需要传入 RelativeLayout.LayoutParams ,第一个参数和第二个参数应该对应。
        .useDefaultIndicator()// 使用默认进度条
        .defaultProgressBarColor() // 使用默认进度条颜色
        .setReceivedTitleCallback(new ChromeClientCallbackManager.ReceivedTitleCallback() {
            @Override
            public void onReceivedTitle(WebView view, String title) {

            }
        }) //设置 Web 页面的 title 回调
        .createAgentWeb()//
        .ready()
        .go(url1);

 


 

 

 

 

 

XTableLayout与ViewPage用FragmentPagerAdapter适配

viewPage.setCurrentItem(1)表示ViewPage里面与XTableLayout的第二项

 

Banner点击事件

 

banner.setOnBannerListener(new OnBannerListener() {
    @Override
    public void OnBannerClick(int position) {
        jump2Activity(String.valueOf(mapList.get(position).get("id")),DetailActivity.class);
    }
});

 

PopWindow遮住状态栏

 

setClippingEnabled(false);

布局中软键盘自动弹回

 

InputMethodManager imm = (InputMethodManager) getApplication().getSystemService(Context.INPUT_METHOD_SERVICE);  imm.hideSoftInputFromWindow(et_talking_content.getWindowToken() , 0);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值