其他分享
首页 > 其他分享> > 【Android春招每日一练】(十三) 剑指4题+Android基础

【Android春招每日一练】(十三) 剑指4题+Android基础

作者:互联网

文章目录

概览

剑指offer:第一个只出现一次的字符、数组中的逆序对、两个链表的第一个公共节点、在排序数组中查找数字 I
Android基础:Android IPC几种方式、Bitmap压缩策略、Android动画总结、进程优先级

剑指offer

1.49 第一个只出现一次的字符

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例 1:
输入:s = "abaccdeff"
输出:'b'
class Solution {
    public char firstUniqChar(String s) {
        HashMap<Character,Boolean> map = new HashMap<>();
        char[] sc = s.toCharArray();
        for(char e:sc){
            map.put(e,!map.containsKey(e));
        }
        for(char e:sc){
            if(map.get(e)) return e;
        }
        return ' ';
    }
}

1.50 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:
输入: [7,5,6,4]
输出: 5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i28yMWSE-1643190562322)(D:\Typora\img\1614274007-rtFHbG-Picture2.png)]

//仅需要在归并排序中添加一行统计数量
class Solution {
   int count = 0;
    public int reversePairs(int[] nums) {
        this.count = 0;
        sort(nums, 0, nums.length - 1,new int[nums.length]);
        return count;
    }

    public void sort(int[] nums,int left,int right,int[] temp){
        if(left < right){
            int mid = (left+right) / 2;//开始递归划分
            sort(nums,left,mid,temp);//归并排序左部分(left,mid)
            sort(nums,mid+1,right,temp);//归并排序右部分(mid+1,right)
            merge(nums,left,mid,right,temp);//合并
        }
    }

    private void merge(int[] nums, int left, int mid, int right, int[] temp) {
        int i = left;//左部分首元素
        int j = mid + 1;//右部分首元素
        int t = 0;
        while(i <=mid && j <=right){//在范围之内
            if(nums[i] <= nums[j]){
                temp[t++] = nums[i++];
            }else{
                count += (mid - i + 1);//只需要这行代码
                temp[t++] = nums[j++];
            }
        }
        while (i <= mid){//右边遍历完事了   左边还剩呢
            temp[t++] = nums[i++];
        }
        while( j <= right){//左边遍历完事了   右边还剩了
            temp[t++] = nums[j++];
        }
        t = 0;//将temp中的元素  全部都copy到原数组里边去
        while (left <=right){
            nums[left++] = temp[t++];
        }
    }
}

1.51 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表**:**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RFnzUBnk-1643190562325)(D:\Typora\img\160_statement.png)]

在节点 c1 开始相交。

//双指针
//你变成我,走过我走过的路。
//我变成你,走过你走过的路。
//然后我们便相遇了...
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA,B = headB;
        while(A != B){
            A = A != null ? A.next : headB;
            B = B != null ? B.next : headA;
        }
        return A;
    }
}

1.52 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
//二分查找
class Solution {
    public int search(int[] nums, int target) {
        return helper(nums, target) - helper(nums, target - 1);
    }
    int helper(int[] nums, int tar) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= tar) i = m + 1;
            else j = m - 1;
        }
        return i;
    }
}

Android基础

Android IPC几种方式

一、使用 Intent

  1. Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle实现了 Parcelable 接口,可以在不同的进程间进行传输。

  2. 在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在Bundle 中附加要传递的数据通过 Intent 发送出去。

二、使用文件共享

  1. Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。

  2. 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。

  3. SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。

三、使用 Messenger

Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。

四、使用 AIDL

Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用 Messenger ,而且Messenger 只适合传递消息,不能跨进程调用服务端的方法。AIDL 可以解决并发和跨进程调用方法的问题,要知道 Messenger 本质上也是 AIDL ,只不过系统做了封装方便上层的调用而已。

AIDL 文件支持的数据类型

五、使用 ContentProvider

用于不同应用间数据共享,和 Messenger 底层实现同样是 Binder 和 AIDL,系统 做了封装,使用简单。 系统预置了许多 ContentProvider ,如通讯录、日程表,需要跨进程访问。

使用方法:继承 ContentProvider 类实现 6 个抽象方法,这六个方法均运行在 ContentProvider 进程中,除 onCreate 运行在主线程里,其他五个方法均由外界回调运行在 Binder 线程池中。

ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。

六、使用 Socket

Bitmap压缩策略

**加载Bitmap的方式 **

Bitmap在Android中指的是一张图片。通过BitmapFactory类提供的四类方法:decodeFile,decodeResource,decodeStream和decodeByteArray,分别从文件系统,资源,输入流和字节数组中加载出一个Bitmap对象,其中decodeFile,decodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。

**BitmapFactory.Options的参数 **

inSampleSize参数

上述四类方法都支持BitmapFactory.Options参数,而Bitmap的按一定采样率进行缩放就是通过BitmapFactory.Options参数实现的,主要用到了inSampleSize参数,即采样率。通过对inSampleSize的设置,对图片的像素的高和宽进行缩放。

当inSampleSize=1,即采样后的图片大小为图片的原始大小。小于1,也按照1来计算。 当inSampleSize>1,即采样后的图片将会缩小,缩放比例为1/(inSampleSize 的二次方)。

例如:一张1024 ×1024像素的图片,采用ARGB8888格式存储,那么内存大小1024×1024×4=4M。如果inSampleSize=2,那么采样后的图片内存大小:512×512×4=1M。

注意:官方文档支出,inSampleSize的取值应该总是2的指数,如1248等。如果外界传入的inSampleSize的值不为2的指数,那么系统会向下取整并选择一个最接近2的指数来代替。比如3,系统会选择2来代替。当时经验证明并非在所有Android版本上都成立。

关于inSampleSize取值的注意事项: 通常是根据图片宽高实际的大小/需要的宽高大小,分别计算出宽和高的缩放比。但应该取其中最小的缩放比,避免缩放图片太小,到达指定控件中不能铺满,需要拉伸从而导致模糊

inJustDecodeBounds参数

我们需要获取加载的图片的宽高信息,然后交给inSampleSize参数选择缩放比缩放。那么如何能先不加载图片却能获得图片的宽高信息,通过inJustDecodeBounds=true,然后加载图片就可以实现只解析图片的宽高信息,并 不会真正的加载图片,所以这个操作是轻量级的。当获取了宽高信息,计算出缩放比后,然后在将inJustDecodeBounds=false,再重新加载图片,就可以加载缩放后的图片。

注意:BitmapFactory获取的图片宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者程序运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果,和Android的资源加载机制有关。

流程

①将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。

②从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。

③根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。

④将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){ 
	BitmapFactory.Options options = new BitmapFactory.Options(); 
    
	options.inJustDecodeBounds = true; 
	//加载图片 
	BitmapFactory.decodeResource(res,resId,options); 
	//计算缩放比 
	options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth); 
	//重新加载图片 
	options.inJustDecodeBounds =false; 
	return BitmapFactory.decodeResource(res,resId,options); 
}

private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) { 
	int height = options.outHeight; 
	int width = options.outWidth; 
	int inSampleSize = 1; 
	if(height>reqHeight||width>reqWidth){ 
		int halfHeight = height/2; 
		int halfWidth = width/2; 
		//计算缩放比,是2的指数 
		while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){ 
			inSampleSize*=2;
    	} 
	}
	return inSampleSize; 
} 

Android动画总结

帧动画

帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放。

帧动画的使用很简单,总共就两个步骤:

1、在res/drawable目录下定义一个XML文件,根节点为系统提供的animation-list,然后放入定义更好的图片;
2、使用AnimationDrawable类播放第一步定义好的Drawable中的图片,形成动画效果;

第一步,创建Drawable文件:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
	android:oneshot="false">
	<item android:drawable="@drawable/image01" android:duration="500"/>
	<item android:drawable="@drawable/image02" android:duration="500"/>
	<item android:drawable="@drawable/image03" android:duration="500"/>
</animation-list>

上述xml中,有些属性我们要了解到:

1、android:oneshot=“false”: 表示是否重复播放动画,还是只播放一次;
2、每个item都有Drawable和duration属性,Drawable表示我们要播放的图片;duration表示这张图播放的时间;

第二步,用AnimationDrawable播放动画:

Button button = (Button) findViewById(R.id.bt_001);
button.setBackgroundResource(R.drawable.frame_animation);//把Drawable设置为button的背景
//拿到这个我们定义的Drawable,实际也就是AnimationDrawable
AnimationDrawable animationDrawable = (AnimationDrawable) button.getBackground();
animationDrawable.start();//开启动画

补间动画(View动画)

补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。 补间动画的实现,一般会采用xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YjOgyEuB-1643190487452)(D:\Typora\img\20190621225246708.png)]

View动画的组合动画–AnimationSet
我们可以使用AnimationSet把View动画的平移、缩放、旋转、渐变都揉在一起,也是既能通过代码实现,也可以通过xml实现

xml实现:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    >
    <scale
        android:duration="3000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0"/>
    <alpha
        android:duration="3000"
        android:fromAlpha="1.0"
        android:toAlpha="0.5" />
    <rotate
        android:fromDegrees="0"
        android:toDegrees="720"
        android:pivotX = "50%"
        android:pivotY="50%"
        android:duration = "3000"
        />
    <translate
        android:fromXDelta="0"
        android:toXDelta="100"
        android:fromYDelta="0"
        android:toYDelta="100" />
</set>

代码实现:

AnimationSet setAnimation = new AnimationSet(true);
    // 特别说明以下情况
    // 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
    // 所以动画不会结束,而是无限循环
    // 所以组合动画的下面两行设置是无效的, 以后设置的为准
    setAnimation.setRepeatMode(Animation.RESTART);
    setAnimation.setRepeatCount(1);// 设置了循环一次,但无效

    // 旋转动画
    Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    rotate.setDuration(1000);
    rotate.setRepeatMode(Animation.RESTART);
    rotate.setRepeatCount(Animation.INFINITE);

    // 平移动画
    Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
            TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
            TranslateAnimation.RELATIVE_TO_SELF,0
            ,TranslateAnimation.RELATIVE_TO_SELF,0);
    translate.setDuration(10000);

    // 透明度动画
    Animation alpha = new AlphaAnimation(1,0);
    alpha.setDuration(3000);
    alpha.setStartOffset(7000);

    // 缩放动画
    Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scale1.setDuration(1000);
    scale1.setStartOffset(4000);

    // 将创建的子动画添加到组合动画里
    setAnimation.addAnimation(alpha);
    setAnimation.addAnimation(rotate);
    setAnimation.addAnimation(translate);
    setAnimation.addAnimation(scale1);
    // 使用
    mButton.startAnimation(setAnimation);

使用场景
1.LayoutAnimation
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,然后,当它的子元素出场时都会具有这种效果。这种效果常用与ListView,有的ListView的每个item都以一定的动画形式出现,就是用到的LayoutAnimation。

2.Activity的切换效果
Activity有默认的切换效果,但是我们可以定制,主要用到overridePendingTransition(int enterAnima, int exitAnima)这个方法:

Intent intent = new Intent(this,TestActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
//注意, 这个方法必须在startActivity或者finish方法之后调用才会生效。

3.Fragment的切换效果
可以使用FragmentTransaction的setCustomAnimation方法添加切换动画。

属性动画

属性动画可以看作是增强版的补间动画,与补间动画的不同之处体现在:

与补间动画类似的是,属性动画也需要定义几个方面的属性:

进程优先级

前台进程

系统中的前台进程并不会很多,而且一般前台进程都不会因为内存不足被杀死。特殊情况除外。当内存低到无法保证所有的前台进程同时运行时,才会选择杀死某个进程。

可视进程

可视进程一般也不会被系统杀死,除非为了保证前台进程的运行不得已而为之。

服务进程

后台进程

后台进程不会影响用户的体验,为了保证前台进程,可视进程,服务进程的运行,系统随时有可能杀死一个后台进程。当一个正确实现了生命周期的activity处于后台被杀死时,如果用户重新启动,会恢复之前的运行状态。

空进程

系统会杀死空进程,但这不会造成影响。空进程的存在无非为了一些缓存,以便于下次可以更快的启动。

总结

1.逐渐摸清算法套路,总结常用模板;
2.知识点很多停留在了解与理解之间,后面总结需要依次自己实现实践。

标签:inSampleSize,动画,nums,十三,缩放,int,春招,进程,Android
来源: https://blog.csdn.net/z18223345669/article/details/122706333