การใช้ getResources () ในคลาสที่ไม่ใช่กิจกรรม


123

ฉันพยายามใช้เมธอด getResources ในคลาสที่ไม่ใช่กิจกรรม ฉันจะได้รับการอ้างอิงไปยังวัตถุ "ทรัพยากร" เพื่อให้สามารถเข้าถึงไฟล์ xml ที่เก็บไว้ในโฟลเดอร์ทรัพยากรได้อย่างไร

ตัวอย่าง:

XmlPullParser xpp = getResources().getXml(R.xml.samplexml);

โดยปกติไม่ใช่ความคิดที่ดีที่จะส่งผ่านContextวัตถุต่างๆใน Android อาจนำไปสู่การรั่วไหลของหน่วยความจำ ดูคำตอบของฉันสำหรับวิธีแก้ปัญหาที่มีความเสี่ยงน้อย
Jason Crosby

คำตอบ:


147

คุณจะต้องส่งผ่านcontextวัตถุไป ไม่thisว่าคุณจะมีการอ้างอิงถึงคลาสในกิจกรรมหรือgetApplicationContext()

public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        RegularClass regularClass = new RegularClass(this);
    }
}

จากนั้นคุณสามารถใช้ในตัวสร้าง (หรือตั้งค่าเป็นตัวแปรอินสแตนซ์):

public class RegularClass(){
    private Context context;

    public RegularClass(Context current){
        this.context = current;
    }

    public findResource(){
        context.getResources().getXml(R.xml.samplexml);
    }
}

โดยที่ตัวสร้างยอมรับContextเป็นพารามิเตอร์


7
โดยปกติไม่ใช่ความคิดที่ดีที่จะส่งผ่านContextวัตถุต่างๆใน Android อาจนำไปสู่การรั่วไหลของหน่วยความจำ
Jason Crosby

28
ตามกฎพื้นฐานทั่วไปแน่นอน แต่ฉันรู้สึกว่านี่ค่อนข้างทำให้เข้าใจผิด Contextวัตถุเป็นสิ่งที่น่ารังเกียจเนื่องจากไม่สามารถมองเห็นได้ชัดเจนในทันทีว่าเป็นแอปพลิเคชันทั้งแบบกว้างหรือทั่วทั้งกิจกรรม หน่วยความจำรั่ว (และล่ม) เกิดขึ้นเมื่อคุณใส่ผิด ตัวอย่างเช่นการจัดหาActivityวัตถุให้กับวัตถุคงที่ซึ่งต้องการContextและกล่าวว่าวัตถุจะไม่ถูกทำลายเมื่อActivityนำไปสู่การActivityคงอยู่หลังจาก onDestroy เนื่องจากไม่สามารถ GCed ได้เนื่องจากวัตถุคงที่อื่นนี้ ใช่มันอาจเป็นอันตรายได้ แต่เมื่อรู้ว่าทำไมจึงอันตรายฉันรู้สึกว่าเป็นเรื่องสำคัญที่จะต้องพูดถึงที่นี่
Dororo

2
^ โดโรโระนี่เป็นหนึ่งในความคิดเห็นที่สำคัญที่สุดที่ฉันเคยอ่าน การใช้บริบทอย่างเหมาะสมมักไม่ค่อยมีการพูดคุยกัน ฉันรู้สึกว่าฉันมีข้อผิดพลาดมากมายที่อธิบายไม่ได้เพราะมัน!
Jonathan Dunn

@ โดโรโระคุณมีข้อเสนอแนะในการปฏิบัติหรือไม่? เราควรพยายามหลีกเลี่ยงการส่งผ่านตัวแปรบริบทหรือไม่? แล้วเราจะทำอะไรได้บ้างเมื่อต้องการ api จากคลาสกิจกรรม?
Alston

35

ไม่ใช่ความคิดที่ดีที่จะส่งContextสิ่งของไปรอบ ๆ ซึ่งมักจะนำไปสู่การรั่วไหลของหน่วยความจำ ข้อเสนอแนะของฉันคืออย่าทำ ฉันสร้างแอพ Android มากมายโดยไม่ต้องส่งบริบทไปยังคลาสที่ไม่ใช่กิจกรรมในแอพ แนวคิดที่ดีกว่าคือการได้รับทรัพยากรที่คุณต้องการเข้าถึงในขณะที่คุณอยู่ในActivityหรือFragmentและเก็บไว้ในชั้นเรียนอื่น จากนั้นคุณสามารถใช้คลาสนั้นในคลาสอื่น ๆ ในแอพของคุณเพื่อเข้าถึงทรัพยากรโดยไม่ต้องผ่านContextวัตถุ


นี่เป็นคำแนะนำที่ดีขอบคุณ มันจะเป็นปัญหาใน SQLiteOpenHelper หรือไม่? ในตัวสร้างคุณต้องส่งบริบท วิธีอื่นไม่สามารถใช้ได้อีกต่อไป แต่ฉันสามารถเก็บไว้ในช่องส่วนตัวได้
Peter

2
@Peter ใช่มีบางคลาสที่ต้องการให้คุณส่งผ่านวัตถุบริบท ดังนั้นจึงเป็นการดีที่สุดที่จะพยายามใช้เฉพาะคลาสเหล่านั้นเช่น SqLiteOpenHelper ในกิจกรรมหรือแฟรกเมนต์เพื่อที่คุณจะได้ไม่ต้องผ่านวัตถุบริบท หากหลีกเลี่ยงไม่ได้เพียงตรวจสอบให้แน่ใจว่าคุณตั้งค่าการอ้างอิงไปยังวัตถุบริบทเป็นโมฆะเมื่อคุณทำเสร็จแล้วเพื่อช่วยลดความเสี่ยงของการรั่วไหลของหน่วยความจำ
Jason Crosby

1
การส่งผ่านวัตถุบริบทไม่ใช่เรื่องเลวร้ายเสมอไปตราบใดที่คุณสามารถตรวจสอบวงจรชีวิตของกิจกรรมได้ ถ้าไม่เช่นนั้นให้ใช้บริบทของแอปพลิเคชันแทนบริบทกิจกรรมโดยใช้ getApplicationContext () เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ ดูstackoverflow.com/questions/7144177/…สำหรับการดึงบริบทแอปพลิเคชัน
FrozenFire

14

มีอีกวิธีหนึ่งโดยไม่ต้องสร้างวัตถุด้วย ตรวจสอบการอ้างอิง ขอบคุณสำหรับ @cristian ด้านล่างฉันเพิ่มขั้นตอนที่กล่าวถึงในข้อมูลอ้างอิงข้างต้น สำหรับฉันฉันไม่ต้องการสร้างวัตถุสำหรับสิ่งนั้นและเข้าถึง ดังนั้นฉันจึงพยายามเข้าถึงgetResources()โดยไม่ต้องสร้างวัตถุ เจอกระทู้นี้ เลยคิดว่าจะเพิ่มเป็นคำตอบ

ทำตามขั้นตอนเพื่อเข้าถึงgetResources()ในคลาสที่ไม่ใช่กิจกรรมwithout passing a contextผ่านอ็อบเจ็กต์

  • สร้าง subclass ของตัวอย่างเช่นApplication public class App extends Application {อ้างอิงรหัสถัดจากขั้นตอน
  • ตั้งค่าandroid:nameแอตทริบิวต์ของ<application>แท็กของคุณในAndroidManifest.xmlto ชี้ไปที่คลาสใหม่ของคุณเช่นandroid:name=".App"
  • ในonCreate()วิธีการของอินสแตนซ์ของแอปบันทึกบริบทของคุณ (เช่นthis) ไปที่สนามคงชื่อและสร้างวิธีการแบบคงที่ผลตอบแทนด้านนี้เช่นappgetContext()
  • ตอนนี้คุณสามารถใช้: App.getContext()เมื่อใดก็ตามที่คุณต้องการรับบริบทจากนั้นเราสามารถใช้App.getContext().getResources()เพื่อรับค่าจากทรัพยากรได้

นี่คือลักษณะที่ควรมี:

public class App extends Application{

    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
    }

    public static Context getContext(){
        return mContext;
    }
}

5

นี่คือคำตอบของฉัน:

public class WigetControl {
private Resources res;

public WigetControl(Resources res) 
{
    this.res = res;
}

public void setButtonDisable(Button mButton)
{
    mButton.setBackgroundColor(res.getColor(R.color.loginbutton_unclickable));
    mButton.setEnabled(false);
}

}

และการโทรอาจเป็นเช่นนี้:

        WigetControl control = new WigetControl(getResources());
        control.setButtonDisable(btNext);

3

ซึ่งสามารถทำได้โดยใช้

context.getResources().getXml(R.xml.samplexml);

นี่เป็นเวทมนตร์สำหรับฉัน ขอบคุณ @ARAsha
Kenny Dabiri

การเดินผ่านContextวัตถุไม่ใช่แนวทางปฏิบัติที่ดีต่อสุขภาพ
Vemuri Pavan

3

เราสามารถใช้บริบทเช่นนี้ลองตอนนี้โดยที่ผู้ปกครองคือ ViewGroup

Context context = parent.getContext();

1

ไม่จำเป็นต้องผ่านบริบทและทำทุกอย่าง ... เพียงแค่ทำสิ่งนี้

Context context = parent.getContext();

แก้ไข: โดยที่พาเรนต์คือ ViewGroup


3
ฉันคาดว่าคุณจะถูกลดคะแนนเนื่องจากสมมติว่ามีตัวแปรสมาชิก "ViewGroup parent" ที่สะดวก ข้อสันนิษฐานที่ค่อนข้างโง่
arnt

1

สิ่งนี้ใช้ได้กับฉันเสมอ:

import android.app.Activity;
import android.content.Context;

public class yourClass {

 Context ctx;

 public yourClass (Handler handler, Context context) {
 super(handler);
    ctx = context;
 }

 //Use context (ctx) in your code like this:
 XmlPullParser xpp = ctx.getResources().getXml(R.xml.samplexml);
 //OR
 final Intent intent = new Intent(ctx, MainActivity.class);
 //OR
 NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
 //ETC...

}

ไม่เกี่ยวข้องกับคำถามนี้ แต่เป็นตัวอย่างการใช้ Fragment เพื่อเข้าถึงทรัพยากรระบบ / กิจกรรมเช่นนี้:

public boolean onQueryTextChange(String newText) {
 Activity activity = getActivity();
 Context context = activity.getApplicationContext();
 returnSomething(newText);
 return false;
}

View customerInfo = getActivity().getLayoutInflater().inflate(R.layout.main_layout_items, itemsLayout, false);
 itemsLayout.addView(customerInfo);

1

ในแอพไกด์นำเที่ยวของหลักสูตร Basic ANdroid ของ Udacity ฉันได้ใช้แนวคิดของ Fragments ฉันติดขัดในขณะที่ประสบปัญหาในการเข้าถึงทรัพยากรสตริงบางอย่างที่อธิบายไว้ในสตริงไฟล์ xml ในที่สุดก็ได้ทางออก

นี่คือชั้นเรียนกิจกรรมหลัก

แพ็คเกจ com.example.android.tourguidekolkata;

import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState)
{
  //lines of code
   //lines of code
    //lines of code
    YourClass adapter = new YourClass(getSupportFragmentManager(), getApplicationContext()); 
    //lines of code
    // getApplicationContext() method passses the Context of main activity to the class TourFragmentPageAdapter 
}
}

นี่คือคลาสที่ไม่ใช่กิจกรรมที่ขยาย FragmentPageAdapter

public class YourClass extends FragmentPagerAdapter {
private String yourStringArray[] = new String[4];
Context context;

public YourClass (FragmentManager fm, Context context)
{
    super(fm);
    this.context = context; // store the context of main activity
    // now you can use this context to access any resource 
    yourStringArray[0] = context.getResources().getString(R.string.tab1);
    yourStringArray[1] = context.getResources().getString(R.string.tab2);
    yourStringArray[2] = context.getResources().getString(R.string.tab3);
    yourStringArray[3] = context.getResources().getString(R.string.tab4);
}
@Override
public Fragment getItem(int position)
 {
 }
@Override
public int getCount() {
return 4;
}

@Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return yourStringArras[position];
}
}

0

ในคลาสธรรมดาให้ประกาศบริบทและรับข้อมูลจากไฟล์จากโฟลเดอร์ res

public class FileData
{
      private Context context;

        public FileData(Context current){
            this.context = current;
        }
        void  getData()
        {
        InputStream in = context.getResources().openRawResource(R.raw.file11);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        //write stuff to get Data

        }
}

ในชั้นเรียนกิจกรรมประกาศเช่นนี้

public class MainActivity extends AppCompatActivity 
{
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        FileData fileData=new FileData(this);
     }

}

0

ฉันมาช้า แต่วิธีแก้ปัญหาที่สมบูรณ์: ตัวอย่างคลาสใช้บริบทเช่นนี้: -

public class SingletonSampleClass {

    // Your cute context
    private Context context;
    private static SingletonSampleClass instance;

  // Pass as Constructor
    private SingletonSampleClass(Context context) {
        this.context = context;
    }

    public synchronized static SingletonSampleClass getInstance(Context context) {
        if (instance == null) instance = new SingletonSampleClass(context);
        return instance;
    }

//At end, don't forgot to relase memory
    public void onDestroy() {
       if(context != null) {
          context = null; 
       }
    }
}

คำเตือน (หน่วยความจำรั่ว)

วิธีแก้ปัญหานี้

ตัวเลือกที่ 1 : แทนที่จะส่งบริบทกิจกรรมเช่นนี้ไปยังคลาส singleton คุณสามารถส่ง applicationContext () ได้

ตัวเลือกที่ 2:หากคุณต้องใช้บริบทกิจกรรมจริงๆเมื่อกิจกรรมถูกทำลายตรวจสอบให้แน่ใจว่าบริบทที่คุณส่งไปยังคลาสซิงเกิลตันถูกตั้งค่าเป็นโมฆะ

หวังว่าจะช่วยได้ .. ∆∆∆∆


0

ใน MainActivity ของคุณ:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(ResourcesHelper.resources == null){
             ResourcesHelper.resources = getResources();
        }
    }
}

ทรัพยากร

public class ResourcesHelper {
    public static Resources resources;
}

จากนั้นใช้งานได้ทุกที่

String s = ResourcesHelper.resources.getString(R.string.app_name);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.